RequireJS 优化器

RequireJS 有一个优化工具,它可以执行以下操作:

  • 将相关的脚本组合成构建层,并通过UglifyJS(默认)或Closure Compiler(使用 Java 时可选)。
  • 通过内联 @import 引用的 CSS 文件并移除注释来优化 CSS。

优化器是Node 和 Nashorn 的 r.js 适配器的一部分,它被设计为在你完成开发并准备部署代码时作为构建或打包步骤运行。

优化器只会合并那些以字符串字面量数组形式传递给顶层 require 和 define 调用的模块,或者是在简化的 CommonJS 包装中的 require('name') 字符串字面量调用所指定的模块。因此,它不会找到通过变量名加载的模块:

var mods = someCondition ? ['a', 'b'] : ['c', 'd'];
require(mods);

但如果像如下方式指定的话,'a' 和 'b' 将会被包括进去:

require(['a', 'b']);

或者:

define(['a', 'b'], function (a, b) {});

此行为允许即使经过优化后仍能动态加载模块。你可以始终通过使用include选项

需求 § 1

来显式添加优化器静态分析找不到的模块。优化器可以使用 Node、带 Rhino 或 Nashorn 的 Java 或浏览器来运行。每个选项的要求如下:

  • Node:(首选)Node0.4.0 或更高版本。
  • Java: Java 1.6或更高版本。
  • 浏览器:自 2.1.2 版起,优化器可以在具有数组扩展功能的 Web 浏览器中运行。虽然优化器的选项与下面显示相同,但它通过 JavaScript 调用而不是命令行选项。它也仅适用于生成优化的单个文件,不适用于目录优化。请参阅浏览器示例。这个选项真正有用的地方是提供基于 Web 的库自定义构建。

对于命令行使用,Node 是首选的执行环境。优化器在Node 下运行得更快

本页上的所有示例命令都假设使用 Node,并在 Linux/OS X 命令行下运行。请参阅r.js README了解如何在 Java 中运行它。

下载§ 2

1) 你可以在下载页面.

下载该工具

> npm install -g requirejs
> r.js -o app.build.js

如果你正在使用带有 NPM 的 Node,则可以通过将 "requirejs" 包作为全局包安装来安装 r.js:r.js.cmd而不是输入r.js。或者,你可以使用DOSKEY:

DOSKEY r.js=r.js.cmd $*

如果你想将 requirejs 作为本地 npm 包安装到某个项目中而非全局:

> npm install requirejs

在这种本地安装的情况下,你可以通过运行项目r.js中的r.js.cmdnode_modules/.bin目录下的文件来运行优化器。

使用本地安装,你也可以在 node 程序中通过函数调用使用优化器

本页其余部分假定 r.js 是从下载页面手动下载的。这是通常最清晰且最具移植性的使用优化器方式。

示例设置§ 3

本页中的示例假设你已经下载并将 r.js 存储在一个与项目目录同级的目录中。r.js 的优化器可以位于任何位置,但你需要相应地调整示例中的路径。

示例设置:

  • appdirectory
    • main.html
    • css
      • common.css
      • main.css
    • scripts
      • require.js
      • main.js
      • one.js
      • two.js
      • three.js
  • r.js(来自下载页面)

main.html 有 require.js 的 script 标签,并通过 require 调用加载 main.js,如下所示:

<!DOCTYPE html>
<html>
    <head>
        <title>My App</title>
        <link rel="stylesheet" type="text/css" href="css/main.css">
        <script data-main="scripts/main" src="scripts/require.js"></script>
    </head>
    <body>
        <h1>My App</h1>
    </body>
</html>

main.js 通过 require 调用加载 one.js、two.js 和 three.js:

require(["one", "two", "three"], function (one, two, three) {
});

main.css 的内容类似如下:

@import url("common.css");

.app {
    background: transparent url(../../img/app.png);
}

基础知识 § 4

命令行参数与构建配置属性可以互换

你可以在命令行上指定选项:

node r.js -o baseUrl=. paths.jquery=some/other/jquery name=main out=main-built.js

或者在构建配置文件中指定。在build.js中,相同的命令行参数可以像如下这样指定:

({
    baseUrl: ".",
    paths: {
        jquery: "some/other/jquery"
    },
    name: "main",
    out: "main-built.js"
})

然后只需将构建配置文件的文件名传递给优化器:

node r.js -o build.js

命令行参数优先于构建配置设置,并且你可以将它们混合使用:

node r.js -o build.js optimize=none

命令行参数语法存在限制。点号被视为对象属性分隔符,以允许类似paths.jquery=lib/jquery这样的参数转换为优化器中的如下结构:

paths: {
    jquery: 'lib/jquery'
}

但这意味着你不能为 "core/jquery.tabs" 的 paths 属性设置值。这不会起作用:paths.core/jquery.tabs=empty:因为它会导致如下错误结构:

paths: {
    'core/jquery': {
        tabs: 'empty:'
    }
}

如果你需要设置类似于 "core/jquery.tabs" 的路径,请使用 build.js 文件,并将构建选项指定为 JavaScript 对象,而不是使用命令行参数。

所有选项列表,请参见所有配置选项.

相对路径解析规则::

一般来说,如果是一个路径,它相对于保存构建选项的 build.js 文件;如果只是使用命令行参数,则相对于当前工作目录。例如属于文件路径的属性有:appDir, dir, mainConfigFile, out, wrap.startFile, wrap.endFile.

对于baseUrl它相对于appDir。如果没有 appDir,则 baseUrl 相对于 build.js 文件,或者如果只是使用命令行参数,则相对于当前工作目录。

对于pathspackages它们相对于baseUrl,就像 require.js 中的一样。

对于那些是模块ID的属性,它们应该使用模块ID而不是文件路径。例如:name, include, exclude, excludeShallow, deps.

在浏览器运行时加载的主要JS模块中的配置设置默认情况下不会被优化器读取由优化器读取

这是因为构建的配置设置可能非常不同,并且拥有多个优化目标。因此,需要为优化器指定一组独立的配置选项。

在优化器1.0.5+版本中,mainConfigFile选项可用于指定运行时配置的位置。如果与主JS文件的路径一起指定,则会解析该文件中的第一个requirejs({}), requirejs.config({}), require({}), or require.config({})找到的内容并用作传递给优化器的配置选项的一部分:

mainConfigFile: 'path/to/main.js'

配置优先级顺序:命令行、构建描述文件、mainConfigFile。换句话说,mainConfigFile配置具有最低优先级。

优化一个JavaScript文件 § 5

使用上面的示例设置,如果您只想优化main.js,您可以使用此命令,从appdirectory/scripts目录内运行以下命令:

node ../../r.js -o name=main out=main-built.js baseUrl=.

这将创建一个名为appdirectory/scripts/main-built.js的文件,它将包含main.js、one.js、two.js和three.js的内容。

通常您应该不要将优化后的文件保存在您的干净项目源代码中。通常您应该将它们保存在一个项目副本中,但为了使本示例更简单,这里将其保存在源代码目录下。更改out=选项为您喜欢的任何目录,该目录应包含一份您的源代码副本。然后,您可以将main-built.js文件名更改为main.js,这样HTML页面将加载该文件的优化版本。

如果您想将require.js包含在main.js源中,可以使用以下类型的命令:

node ../../r.js -o baseUrl=. paths.requireLib=../../require name=main include=requireLib out=main-built.js

由于"require"是一个保留依赖名称,您需要创建一个"requireLib"依赖项并将其映射到require.js文件。

一旦完成该优化,您可以将脚本标签更改为引用"main-built.js"而不是"require.js",这样优化后的项目只需要进行一次脚本请求。

如果您希望包装生成的文件以使其能够在没有类似RequireJS这样的AMD加载器的页面上使用,请参阅优化常见问题.

快速开发的浅层排除§ 6

您可以使用单个JavaScript文件优化方法来加快您的开发体验。通过将项目中的所有模块(当前正在开发的模块除外)优化成一个文件,您可以快速重新加载浏览器中的项目,但仍可以选择对某个模块进行细粒度调试。

你可以通过使用excludeShallow选项来实现这一点。使用上述示例设置假设您当前正在构建或调试two.js。您可以使用如下优化命令:

node ../../r.js -o name=main excludeShallow=two out=main-built.js baseUrl=.

如果不希望对main-build.js文件进行压缩,请在上述命令中添加optimize=none参数。

然后配置HTML页面加载main-built.js文件而不是main.js,通过将"main"所使用的路径配置为"main-built":

<!DOCTYPE html>
<html>
    <head>
        <title>My App</title>
        <link rel="stylesheet" type="text/css" href="css/main.css">
        <script src="scripts/require.js"></script>
        <script>
            require.config({
                paths: {
                    //Comment out this line to go back to loading
                    //the non-optimized main.js source file.
                    "main": "main-built"
                }
            });
            require(["main"]);
        </script>
    </head>
    <body>
        <h1>My App</h1>
    </body>
</html>

现在,当页面加载时,对于"main"的require()将加载main-built.js文件。因为excludeShallow仅排除two.js,two.js仍将以单独文件的形式加载,这允许您在浏览器的调试器中看到它作为一个单独的文件,从而可以设置断点并更好地跟踪其独立更改。

empty: 路径用于网络/CDN资源§ 7

您可能有希望从内容分发网络 (CDN)或其它域上的服务器加载的脚本。

优化器无法加载网络资源,所以如果您希望将其包含在构建中,请务必创建一个paths配置将文件映射到模块名称。然后,在运行优化器时,下载CDN脚本并传递一个paths配置给优化器,将模块名称映射到本地文件路径。

然而,您很可能并不希望将该资源包含在构建中。如果该脚本没有任何依赖项,或者您不希望包含它的依赖项,或者将以其他方式包含它们,那么您可以在paths配置中使用特殊的'empty:'方案在优化时跳过该文件。

在您的main.js文件中,创建一个paths配置,为该脚本分配一个模块名称。即使该脚本未通过define()调用来定义模块也可以这样做。paths配置只是用于将短模块/脚本ID映射到URL。这允许您为优化使用不同的paths配置。在main.js中:

requirejs.config({
    paths: {
        'jquery': 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min'
    }
});

require(['jquery'], function ($) {
});

然后,在运行优化器时,在paths配置中使用'empty:':

node ../../r.js -o name=main out=main-built.js baseUrl=. paths.jquery=empty:

或者,在一个构建描述文件:

({
    baseUrl: ".",
    name: "main",
    out: "main-built.js",
    paths: {
        jquery: "empty:"
    }
})

§ 8

使用上面的示例设置,如果您只想优化main.css,您可以使用此命令,从appdirectory/css目录内运行以下命令:

node ../../r.js -o cssIn=main.css out=main-built.css

这将创建一个名为appdirectory/css/main-build.css的文件,该文件将包含main.css的内容,url()路径会被正确调整,并删除注释内容。

有关避免将优化文件保存在原始源树中的说明,请参见优化一个JavaScript文件中关于避免将优化文件保存在原始源代码树中的说明。此处只是为了简化示例。

注意:url()路径修复始终会根据cssIn构建选项路径进行修复,而不是out构建选项。

优化整个项目§ 9

优化器可以通过使用构建配置文件来优化项目中的所有 CSS 和 JS 文件。

创建一个构建配置文件,将其命名为 app.build.js,并将其放在scripts目录中。app.build.js 文件可以放在任何位置,但在下面的示例中请确保路径正确调整——所有路径都是相对于 app.build.js 所在的位置。示例 app.build.js:

({
    appDir: "../",
    baseUrl: "scripts",
    dir: "../../appdirectory-build",
    modules: [
        {
            name: "main"
        }
    ]
})

此构建配置文件告诉 RequireJS 将所有appdirectory内容复制到名为appdirectory-build的同级目录中,并对appdirectory-build目录应用所有优化操作。强烈建议你使用不同于源目录的输出目录——否则优化器可能会覆盖你的源代码而导致问题。

RequireJS 会使用baseUrl来解析任意模块名称的路径。baseUrl应该是相对于appDir.

的路径。modules数组中指定你想要优化的模块名称,在示例中为 "main"。"main" 将映射到项目中的appdirectory/scripts/main.js。构建系统将追踪 main.js 的依赖项,并将它们注入到appdirectory-build/scripts/main.js文件中。

它也将优化在appdirectory-build.

中找到的任何 CSS 文件。appdirectory/scripts目录内运行以下命令:

node ../../r.js -o app.build.js

要运行构建,请从appdirectory-build构建完成后,你可以使用

优化多页面项目§ 10

requirejs/example-multipage是一个多页面项目的示例,该项目共享通用配置和共同的优化构建层。

加速选项§ 11

优化器的默认设置是执行最安全、最稳健的操作,以避免构建后出现意外情况。不过,根据你的项目设置,你可能希望关闭其中某些功能以加快构建速度:

  • 以便 define() 调用能正确规范化,从而经受住压缩处理。如果你是通过optimize设置为"none".
  • none。modules选项中指定的构建层,而不压缩构建输出目录中的其他 JS 文件,则可以将skipDirOptimize设置为true.
  • true。dir指定的输出目录以保持整洁。一些构建选项(如onBuildWrite)将以一种对相同文件重复执行时有风险的方式来修改输出目录。但是,如果你执行的是简单的构建,除了构建层压缩之外没有额外的文件转换操作,则可以将keepBuildDir设置为truetrue

自版本 2.1.2 起,如果optimize设置为"none"none"none"noneoptimize并且计划在优化器运行后对生成文件进行压缩,则应将normalizeDirDefines设置为"all"trueoptimize选项来进行压缩,则无需关心此选项的设置。

与 has.js 的集成§ 12

has.js是一款出色的工具,可为你的项目轻松添加特性检测支持。优化器也提供了一些支持来优化 has.js 测试的代码路径。

如果你的代码使用如下测试:


if (has("someThing")) {
    //use native someThing
} else {
    //do some workaround
}

你可以在构建配置中定义一个has对象,并为某些 has() 测试赋予 true 或 false 值,优化器会将这些 has() 测试替换为对应的布尔值。

如果你的构建配置如下所示:


({
    baseUrl: ".",
    name: "hasTestModule",
    out: "builds/hasTestModule.js",
    has: {
        someThing: true
    }
})

那么优化器将把上面的代码样例转换为:


if (true) {
    //use native someThing
} else {
    //do some workaround
}

然后,如果你在 r.js 0.26.0 或更高版本中使用默认的 optimize 设置 "uglify",或者将optimize设置为 "closure"(当在 Java 下运行时),压缩工具将会优化掉无用代码分支!因此你可以针对一组 has() 测试定制并优化你的代码构建。

源码地图§ 13

版本 2.1.6 及更高版本对source maps,则只能映射压缩、合并后的代码到未压缩的合并代码(closure 仅在 Java 与 Rhino 下运行时可用)。开发者工具中显示的未压缩文件将带有 ".src.js" 的扩展名。optimize设置为"uglify2"uglify"closure"closure

不过还有办法在压缩后的源码中保留部分许可协议注释generateSourceMaps设置为truetrue。preserveLicenseComments设置为false. 0。.

优化器已经支持sourceURL功能(通过将useSourceUrl设置为truetrue

所有配置选项第14节

在requirejs/build目录中有一个example.build.js文件,其中详细列出了所有允许的优化器配置选项。

部署技术第15节

r.js优化器设计为提供一些基本功能,可以通过在其之上添加其他代码来用于不同的部署场景。请参阅部署技术维基页面了解如何以这种方式使用优化器的思路。

常见陷阱第16节

如果您在下面的示例中遇到问题,以下是一些可能导致问题的常见陷阱:

不要将输出目录指定为JavaScript源文件区域内部

例如,如果您的baseUrl是“js”,而构建输出进入“js/build”,那么每次优化运行时生成的额外嵌套文件可能会导致问题。此建议仅适用于非单文件优化的情况。

避免使用超出baseUrl范围的优化名称

例如,如果您的baseUrl是“js”,而您的优化目标为:

name: '../main'

优化可能会覆盖或把文件放置到输出目录之外。在这种情况下,请创建一个paths配置,将该文件映射到本地名称,比如:

paths: {
    main: '../main'
}

然后使用name:

name: 'main'

作为优化目标。

注意shim配置的构建限制。特别需要注意的是,您不能从CDN加载shim库的依赖项。请参阅shim配置章节了解更多详情。