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:**(首选)Node 0.4.0 或更高版本。
  • **Java:**Java 1.6 或更高版本。
  • **浏览器:**从 2.1.2 版本开始,优化器可以在具有 数组扩展的 Web 浏览器中运行。虽然优化器选项与下面显示的相同,但它是通过 JavaScript 而不是命令行选项调用的。它也只适用于生成优化的单个文件,而不适用于目录优化。请参阅浏览器示例。此选项实际上仅适用于提供基于 Web 的库自定义构建。

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

此页面中的所有示例命令都假定使用 Node,并在 Linux/OS X 命令行上运行。有关如何在 Java 中运行它的信息,请参阅r.js 自述文件

下载§ 2

1) 您可以在下载页面上下载该工具。

2) 如果您将 Node 与 NPM 一起使用,则可以将 r.js 作为 NPM 中“requirejs”包的一部分全局安装

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

如果在 Windows 上,您可能需要键入 r.js.cmd 而不是 r.js。或者,您可以使用DOSKEY

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

如果您想将 requirejs 作为 npm 包本地安装在项目中,而不是全局安装

> npm install requirejs

通过此本地安装,您可以通过运行项目 node_modules/.bin 目录中的 r.jsr.js.cmd 文件来运行优化器。

通过本地安装,您还可以通过 node 程序中的函数调用来使用优化器

本页面的其余部分假设 r.js 只是从下载页面手动下载的。这通常是使用优化器的最清晰、最便携的方式。

示例设置§ 3

本页面中的示例假设您已将 r.js 下载并保存到与项目目录同级的目录中。作为 r.js 一部分的优化器可以位于您想要的任何位置,但您可能需要在这些示例中相应地调整路径。

示例设置

  • 应用程序目录
    • main.html
    • css
      • common.css
      • main.css
    • scripts
      • require.js
      • main.js
      • one.js
      • two.js
      • three.js
  • r.js(来自下载页面的 r.js 优化器)

main.html 具有 require.js 的脚本标签,并通过 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.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 文件,或者如果仅使用命令行参数,则相对于当前工作目录。

对于 **paths** 和 **packages**,它们相对于 **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”,并且您的优化项目只需要发出一个脚本请求。

如果要包装构建的文件,以便可以在没有 AMD 加载器(如 RequireJS)的页面中使用它,请参阅优化常见问题解答

用于快速开发的浅层排除§ 6

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

您可以使用 excludeShallow 选项来做到这一点。使用上面的示例设置,假设您当前正在构建或调试 two.js。您可以使用以下优化命令

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

如果您不希望对 main-build.js 文件进行压缩,请在上述命令中传递 optimize=none

然后通过将“main”的路径配置为“main-built”,将 HTML 页面配置为加载 main-built.js 文件而不是 main.js

<!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)或不同域上的任何其他服务器加载。

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

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

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

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

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

然后,在运行优化器时,对路径配置使用 '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:"
    }
})

优化一个 CSS 文件§ 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 是一个多页项目示例,但共享一个通用配置和一个通用优化构建层。

Turbo 选项§ 11

优化器的默认设置是执行最安全、最健壮的操作集,以避免构建后出现意外。但是,根据您的项目设置,您可能希望关闭其中一些功能以加快构建速度

  • 最大的时间消耗是压缩。如果您只是将构建作为开发工作流程的一部分,请将 optimize 设置为 "none"
  • 如果正在进行整个项目优化,但只想压缩 modules 选项中指定的构建层,而不是构建输出目录中的其余 JS 文件,则可以将 skipDirOptimize 设置为 true
  • 通常,每次运行整个项目优化都会删除 dir 指定的输出构建目录以保持清洁。某些构建选项(如 onBuildWrite)将以对相同文件执行两次操作很危险的方式修改输出目录。但是,如果您正在执行没有除构建层压缩之外的额外文件转换的简单构建,则可以将 keepBuildDir 设置为 true 以在运行之间保留构建目录。然后,只会复制在构建运行之间已更改的文件。

从版本 2.1.2 开始,如果 optimize 设置为 "none",优化器将默认采用一些速度捷径。但是,如果您对 optimize 使用 "none" 并且您计划在优化器运行后压缩构建文件,则应将 normalizeDirDefines 设置为 "all",以便正确规范化 define() 调用以承受压缩。如果您正在通过 optimize 选项进行压缩,则无需担心设置此选项。

与 has.js 集成§ 12

has.js 是一个很棒的工具,可以为您的项目添加简单的功能检测。优化器对优化 has.js 测试的代码路径有一些支持。

如果您的代码使用如下测试


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

您可以在构建配置中定义一个 has 对象,其中包含一些 has() 测试的 true 或 false 值,优化器会将 has() 测试替换为 true 或 false 值。

如果您的构建配置文件如下所示


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

然后优化器会将上面的代码示例转换为


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

然后,如果您在 r.js 0.26.0 或更高版本中使用默认的“uglify”优化设置,或者 optimize 设置为“closure”(在Java 下运行时),压缩器将优化掉死代码分支!因此,您可以对代码进行自定义构建,针对一组 has() 测试进行优化。

源映射§ 13

版本 2.1.6 及更高版本对源映射提供实验性支持。它适用于将压缩的捆绑代码映射到未压缩的单独模块,并且仅当 optimize 设置为 "uglify2" 时才有效。optimize 设置为 "closure" 仅允许将压缩的捆绑代码映射到未压缩的捆绑代码(closure 仅在使用 Rhino 在 Java 下运行时可用)。未压缩的文件将在开发工具中显示为“.src.js”文件扩展名。

要启用源映射生成,请将 generateSourceMaps 设置为 true。由于压缩器需要完全控制压缩文件以生成源映射,因此应将 preserveLicenseComments 显式设置为 false有一种方法可以在压缩源中获取一些许可证注释

优化器支持sourceURL(通过将 useSourceUrl 设置为 true),用于将组合模块作为单个文件进行调试。但是,这仅适用于未压缩的代码。源映射将压缩文件转换为未压缩版本。将 useSourceUrl 与 generateSourceMaps 一起使用是没有意义的,因为 useSourceUrl 需要将源值作为字符串,这会阻止与 generateSourceMaps 结合使用的有用压缩。

所有配置选项§ 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: 'main'

作为优化目标。

注意 shim 配置的构建限制。 特别是,您不能从 CDN 加载 shimmed 库的依赖项。有关更多信息,请参阅shim 配置部分