用法 § 1

加载 JavaScript 文件 § 1.1

RequireJS 采用与传统 <script> 标签不同的脚本加载方法。虽然它也可以快速运行并进行良好的优化,但其主要目标是鼓励模块化代码。为此,它鼓励使用模块 ID 而不是 URL 来表示脚本标签。

RequireJS 加载所有相对于 baseUrl 的代码。baseUrl 通常设置为与用于加载页面顶级脚本的 data-main 属性中使用的脚本相同的目录。data-main 属性 是 require.js 将检查以开始脚本加载的特殊属性。此示例最终将使用 scripts 作为 baseUrl

<!--This sets the baseUrl to the "scripts" directory, and
    loads a script that will have a module ID of 'main'-->
<script data-main="scripts/main.js" src="scripts/require.js"></script>

或者,可以通过 RequireJS 配置 手动设置 baseUrl。如果没有显式配置且未使用 data-main,则默认 baseUrl 是包含运行 RequireJS 的 HTML 页面的目录。

RequireJS 默认还假设所有依赖项都是脚本,因此它不希望在模块 ID 上看到尾随的“.js”后缀。RequireJS 在将模块 ID 转换为路径时会自动添加它。使用 paths 配置,您可以设置一组脚本的位置。与传统的 <script> 标签相比,所有这些功能都允许您对脚本使用更短的字符串。

有时您可能确实希望直接引用脚本,而不符合用于查找脚本的“baseUrl + paths”规则。如果模块 ID 具有以下任一特征,则该 ID 将不会通过“baseUrl + paths”配置传递,而只会被视为相对于文档的常规 URL

  • 以“.js”结尾。
  • 以“/”开头。
  • 包含 URL 协议,例如“http:”或“https:”。

但总的来说,最好使用 baseUrl 和“paths”配置来设置模块 ID 的路径。这样做可以让您在优化构建时更灵活地重命名和配置不同位置的路径。

同样,为了避免大量的配置,最好避免脚本的深层文件夹层次结构,而是将所有脚本都保留在 baseUrl 中,或者如果您想将库/供应商提供的代码与应用程序代码分开,请使用如下目录布局

  • www/
    • index.html
    • js/
      • app/
        • sub.js
      • lib/
        • jquery.js
        • canvas.js
      • app.js
      • require.js

在 index.html 中

<script data-main="js/app.js" src="js/require.js"></script>

在 app.js 中

requirejs.config({
    //By default load any module IDs from js/lib
    baseUrl: 'js/lib',
    //except, if the module ID starts with "app",
    //load it from the js/app directory. paths
    //config is relative to the baseUrl, and
    //never includes a ".js" extension since
    //the paths config could be for a directory.
    paths: {
        app: '../app'
    }
});

// Start the main app logic.
requirejs(['jquery', 'canvas', 'app/sub'],
function   ($,        canvas,   sub) {
    //jQuery, canvas and the app/sub module are all
    //loaded and can be used here now.
});

请注意,作为该示例的一部分,像 jQuery 这样的供应商库在其文件名中没有版本号。如果您想跟踪版本信息,建议将其存储在单独的文本文件中,或者如果您使用像 volo 这样的工具,它会在 package.json 中标记版本信息,但将文件保留在磁盘上作为“jquery.js”。这允许您拥有最少的配置,而不必为每个库在“paths”配置中都添加一个条目。例如,将“jquery”配置为“jquery-1.7.2”。

理想情况下,您加载的脚本将是通过调用 define() 定义的模块。但是,您可能需要使用一些传统的/遗留的“浏览器全局”脚本,这些脚本不通过 define() 表达它们的依赖关系。对于这些脚本,您可以使用 shim 配置。以正确表达它们的依赖关系。

如果您没有表达依赖关系,则可能会出现加载错误,因为 RequireJS 会异步且无序地加载脚本以提高速度。

data-main 入口点 § 1.2

data-main 属性是 require.js 将检查以开始脚本加载的特殊属性

<!--when require.js loads it will inject another script tag
    (with async attribute) for scripts/main.js-->
<script data-main="scripts/main" src="scripts/require.js"></script>

您通常会使用 data-main 脚本来 设置配置选项,然后加载第一个应用程序模块。注意:require.js 为您的 data-main 模块生成的脚本标签包含 async 属性。这意味着您不能假设 data-main 脚本的加载和执行将在同一页面中稍后引用的其他脚本之前完成。

例如,当在稍后 require() 之前未设置“foo”模块的 require.config 路径时,此安排将随机失败

<script data-main="scripts/main" src="scripts/require.js"></script>
<script src="scripts/other.js"></script>
// contents of main.js:
require.config({
    paths: {
        foo: 'libs/foo-1.1.3'
    }
});
// contents of other.js:

// This code might be called before the require.config() in main.js
// has executed. When that happens, require.js will attempt to
// load 'scripts/foo.js' instead of 'scripts/libs/foo-1.1.3.js'
require(['foo'], function(foo) {

});

如果您想在 HTML 页面中执行 require() 调用,那么最好不要使用 data-main。data-main 仅适用于页面只有一个主入口点(即 data-main 脚本)的情况。对于想要进行内联 require() 调用的页面,最好将这些调用嵌套在用于配置的 require() 调用中

<script src="scripts/require.js"></script>
<script>
require(['scripts/config'], function() {
    // Configuration loaded now, safe to do other require calls
    // that depend on that config.
    require(['foo'], function(foo) {

    });
});
</script>

定义模块 § 1.3

模块与传统脚本文件不同,它定义了一个范围明确的对象,避免污染全局命名空间。它可以显式列出其依赖项并获取这些依赖项的句柄,而无需引用全局对象,而是将依赖项作为参数接收定义模块的函数。RequireJS 中的模块是 模块模式 的扩展,其优点是不需要全局变量来引用其他模块。

RequireJS 的模块语法允许模块尽可能快地加载,甚至可以无序加载,但会按照正确的依赖顺序进行评估,并且由于不创建全局变量,因此可以在 页面中加载模块的多个版本

(如果您熟悉或正在使用 CommonJS 模块,那么另请参阅 CommonJS 注意事项,以获取有关 RequireJS 模块格式如何映射到 CommonJS 模块的信息)。

磁盘上的每个文件应该只有一个模块定义。可以通过 优化工具 将模块分组到优化的包中。

简单的名称/值对 § 1.3.1

如果模块没有任何依赖项,并且它只是一组名称/值对,则只需将一个对象字面量传递给 define()

//Inside file my/shirt.js:
define({
    color: "black",
    size: "unisize"
});

定义函数 § 1.3.2

如果模块没有依赖项,但需要使用函数来执行一些设置工作,则定义自身,将函数传递给 define()

//my/shirt.js now does setup work
//before returning its module definition.
define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});

具有依赖关系的定义函数§ 1.3.3

如果模块具有依赖项,则第一个参数应该是依赖项名称数组,第二个参数应该是定义函数。加载所有依赖项后,将调用该函数来定义模块。该函数应返回一个定义模块的对象。依赖项将作为函数参数传递给定义函数,其顺序与依赖项数组中的顺序相同

//my/shirt.js now has some dependencies, a cart and inventory
//module in the same directory as shirt.js
define(["./cart", "./inventory"], function(cart, inventory) {
        //return an object to define the "my/shirt" module.
        return {
            color: "blue",
            size: "large",
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

在此示例中,创建了一个 my/shirt 模块。它依赖于 my/cart 和 my/inventory。在磁盘上,文件结构如下

  • my/cart.js
  • my/inventory.js
  • my/shirt.js

上面的函数调用指定了两个参数,“cart”和“inventory”。这些是由“./cart”和“./inventory”模块名称表示的模块。

在加载 my/cart 和 my/inventory 模块之前,不会调用该函数,并且该函数会将模块作为“cart”和“inventory”参数接收。

强烈建议不要使用定义全局变量的模块,以便页面的同一时间可以存在模块的多个版本(请参阅高级用法)。 此外,函数参数的顺序应与依赖项的顺序相匹配。

函数调用返回的对象定义了“my/shirt”模块。 通过以这种方式定义模块,“my/shirt”不会作为全局对象存在。

将模块定义为函数§ 1.3.4

模块不必返回对象。 允许函数返回任何有效值。 这是一个返回函数作为其模块定义的模块

//A module definition inside foo/title.js. It uses
//my/cart and my/inventory modules from before,
//but since foo/title.js is in a different directory than
//the "my" modules, it uses the "my" in the module dependency
//name to find them. The "my" part of the name can be mapped
//to any directory, but by default, it is assumed to be a
//sibling to the "foo" directory.
define(["my/cart", "my/inventory"],
    function(cart, inventory) {
        //return a function to define "foo/title".
        //It gets or sets the window title.
        return function(title) {
            return title ? (window.title = title) :
                   inventory.storeName + ' ' + cart.name;
        }
    }
);

使用简化的 CommonJS 包装器定义模块§ 1.3.5

如果您希望重用一些以传统的 CommonJS 模块格式 编写的代码,则可能难以将其重新转换为上面使用的依赖项数组,并且您可能更喜欢将依赖项名称直接与用于该依赖项的局部变量对齐。 对于这些情况,您可以使用 简化的 CommonJS 包装器

define(function(require, exports, module) {
        var a = require('a'),
            b = require('b');

        //Return the module value
        return function () {};
    }
);

此包装器依赖于 Function.prototype.toString() 来提供函数内容的有用字符串值。 这不适用于某些设备,例如 PS3 和一些较旧的 Opera 移动浏览器。 使用 优化器 以数组格式提取依赖项,以便在这些设备上使用。

更多信息可在 CommonJS 页面“为什么选择 AMD 页面”中的“Sugar”部分 中找到。

使用名称定义模块§ 1.3.6

您可能会遇到一些 define() 调用,这些调用包含模块的名称作为 define() 的第一个参数

    //Explicitly defines the "foo/title" module:
    define("foo/title",
        ["my/cart", "my/inventory"],
        function(cart, inventory) {
            //Define foo/title object in here.
       }
    );

这些通常由 优化工具 生成。 您可以自己显式命名模块,但这会降低模块的可移植性——如果您将文件移动到另一个目录,则需要更改名称。 通常最好避免在代码中为模块命名,而让优化工具刻录模块名称。 优化工具需要添加名称,以便可以在一个文件中捆绑多个模块,以便在浏览器中更快地加载。

其他模块说明§ 1.3.7

每个文件一个模块。:鉴于模块名称到文件路径查找算法的性质,每个 JavaScript 文件应该只定义一个模块。 您应该只使用 优化工具 将多个模块分组到优化文件中。

define() 中的相对模块名称:对于可以在 define() 函数调用中发生的 require("./relative/name") 调用,请确保请求“require”作为依赖项,以便正确解析相对名称

define(["require", "./relative/name"], function(require) {
    var mod = require("./relative/name");
});

或者更好的是,使用可用于 转换 CommonJS 模块的缩短语法

define(function(require) {
    var mod = require("./relative/name");
});

此表单将使用 Function.prototype.toString() 查找 require() 调用,并将它们与“require”一起添加到依赖项数组中,以便代码可以使用相对路径正常工作。

如果您正在目录中创建几个模块,则相对路径非常有用,这样您就可以与其他人或其他项目共享该目录,并且您希望能够处理该目录中的兄弟模块,而无需知道目录的名称。

相对模块名称相对于其他名称,而不是路径:加载器按名称而不是按路径存储模块。 因此,对于相对名称引用,这些引用是相对于进行引用的模块名称解析的,然后如果需要加载,则该模块名称或 ID 将转换为路径。 'compute' 包的示例代码,其中包含 'main' 和 'extras' 模块

* lib/
    * compute/
        * main.js
        * extras.js

其中 main.js 模块如下所示

define(["./extras"], function(extras) {
    //Uses extras in here.
});

如果这是路径配置

require.config({
    baseUrl: 'lib',
    paths: {
      'compute': 'compute/main'
    }
});

并且执行了 require(['compute']),则 lib/compute/main.js 将具有模块名称“compute”。 当它请求 './extras' 时,它会解析为相对于 'compute',所以 'compute/./extras',它会规范化为 'extras'。 由于该模块名称没有路径配置,因此生成的路径将是 'lib/extras.js',这是不正确的。

对于这种情况,包配置 是一个更好的选择,因为它允许将主模块设置为“compute”,但在内部加载器将使用 ID“compute/main”存储模块,以便 './extras' 的相对引用有效。

另一种选择是在 lib/compute.js 处构造一个模块,它只是 define(['./compute/main'], function(m) { return m; });,则不需要路径或包配置。

或者,不要设置该路径或包配置,并将顶级 require 调用执行为 require(['compute/main'])

生成相对于模块的 URL:您可能需要生成相对于模块的 URL。 为此,请请求“require”作为依赖项,然后使用 require.toUrl() 生成 URL

define(["require"], function(require) {
    var cssUrl = require.toUrl("./style.css");
});

控制台调试:如果您需要使用已通过 JavaScript 控制台中的 require(["module/name"], function(){}) 调用加载的模块,则可以使用仅使用模块字符串名称来获取它的 require() 形式

require("module/name").callSomeFunction()

请注意,这仅在“module/name”先前通过异步版本的 require 加载时才有效:require(["module/name"])。 如果使用相对路径,例如 './module/name',则这些路径仅在 define 内部有效

循环依赖§ 1.3.8

如果您定义了循环依赖关系(“a”需要“b”,“b”需要“a”),那么在这种情况下,当调用“b”的模块函数时,它将获得“a”的未定义值。 “b”可以在定义模块后使用 require() 方法稍后获取“a”(请确保将 require 指定为依赖项,以便使用正确的上下文来查找“a”)

//Inside b.js:
define(["require", "a"],
    function(require, a) {
        //"a" in this case will be null if "a" also asked for "b",
        //a circular dependency.
        return function(title) {
            return require("a").doSomething();
        }
    }
);

通常,您不需要使用 require() 来获取模块,而是依赖于作为参数传递给函数的模块。 循环依赖很少见,通常表明您可能需要重新考虑设计。 但是,有时需要它们,在这种情况下,请使用上面指定的 require()。

如果您熟悉 CommonJS 模块,则可以改用exports 为模块创建一个空对象,该对象可立即供其他模块引用。 通过在循环依赖的两侧都这样做,您可以安全地保留另一个模块。 这仅在每个模块都导出一个对象作为模块值而不是函数时才有效

//Inside b.js:
define(function(require, exports, module) {
    //If "a" has used exports, then we have a real
    //object reference here. However, we cannot use
    //any of "a"'s properties until after "b" returns a value.
    var a = require("a");

    exports.foo = function () {
        return a.bar();
    };
});

或者,如果您使用依赖项数组方法,请请求特殊的 'exports' 依赖项:

//Inside b.js:
define(['a', 'exports'], function(a, exports) {
    //If "a" has used exports, then we have a real
    //object reference here. However, we cannot use
    //any of "a"'s properties until after "b" returns a value.

    exports.foo = function () {
        return a.bar();
    };
});

指定 JSONP 服务依赖项§ 1.3.9

JSONP 是一种在 JavaScript 中调用某些服务的方式。 它可以跨域工作,并且是一种调用服务的既定方法,只需要通过脚本标签进行 HTTP GET。

要在 RequireJS 中使用 JSONP 服务,请指定“define”作为回调参数的值。 这意味着您可以像获取模块定义一样获取 JSONP URL 的值。

这是一个调用 JSONP API 端点的示例。 在此示例中,JSONP 回调参数称为“callback”,因此“callback=define”告诉 API 将 JSON 响应包装在“define()”包装器中

require(["http://example.com/api/data.json?callback=define"],
    function (data) {
        //The data object will be the API response for the
        //JSONP data call.
        console.log(data);
    }
);

这种 JSONP 的使用应该限于用于初始应用程序设置的 JSONP 服务。 如果 JSONP 服务超时,则意味着您通过 define() 定义的其他模块可能不会执行,因此错误处理不可靠。

仅支持作为 JSON 对象的 JSONP 返回值。 数组、字符串或数字形式的 JSONP 响应将不起作用。

此功能不应用于长轮询 JSONP 连接——处理实时流的 API。 这些类型的 API 在收到每个响应后应该进行更多脚本清理,并且 RequireJS 只会获取一次 JSONP URL——在 require() 或 define() 调用中随后使用相同的 URL 作为依赖项将获得缓存值。

加载 JSONP 服务中的错误通常通过服务的超时来显示,因为脚本标签加载没有提供太多关于网络问题的详细信息。 要检测错误,您可以覆盖 requirejs.onError() 以获取错误。 处理错误 部分提供了更多信息。

取消定义模块§ 1.3.10

有一个全局函数 requirejs.undef(),它允许取消定义模块。 它将重置加载器的内部状态以忘记模块的先前定义。

但是,它不会从已经定义的其他模块中删除该模块,并且在这些模块执行时将该模块作为依赖项处理。 因此,它实际上只适用于在没有其他模块处理模块值的情况下处理错误情况,或者作为将来可能使用该模块的任何模块加载的一部分。 有关示例,请参阅 errback 部分

如果您想对取消定义工作进行更复杂的依赖图分析,半私有的 onResourceLoad API 可能会有所帮助。

机制 § 2

RequireJS 使用 head.appendChild() 将每个依赖项作为脚本标签加载。

RequireJS 等待所有依赖项加载,确定调用定义模块的函数的正确顺序,然后在调用这些函数的依赖项后调用模块定义函数。 请注意,由于其子依赖关系和网络加载顺序,给定模块定义函数的依赖关系可以按任何顺序调用。

在具有同步加载的服务器端 JavaScript 环境中使用 RequireJS 应该像重新定义 require.load() 一样简单。 构建系统会执行此操作,该环境的 require.load 方法可以在 build/jslib/requirePatch.js 中找到。

将来,此代码可能会被拉入 require/ 目录中,作为您可以在环境中加载的可选模块,以根据主机环境获得正确的加载行为。

配置选项 § 3

在顶级 HTML 页面(或未定义模块的顶级脚本文件)中使用 require() 时,可以将配置对象作为第一个选项传递

<script src="scripts/require.js"></script>
<script>
  require.config({
    baseUrl: "/another/path",
    paths: {
        "some": "some/v1.0"
    },
    waitSeconds: 15
  });
  require( ["some/module", "my/module", "a.js", "b.js"],
    function(someModule,    myModule) {
        //This function will be called when all the dependencies
        //listed above are loaded. Note that this function could
        //be called before the page is loaded.
        //This callback is optional.
    }
  );
</script>

您也可以从您的 数据主入口点 调用 require.config,但请注意,数据主脚本是异步加载的。 避免其他错误地假设数据主脚本及其 require.config 将始终在其脚本加载之前执行的入口点脚本。

此外,您可以在加载 require.js 之前 将 config 对象定义为全局变量 require,并自动应用这些值。 此示例指定了一些要在 require.js 定义 require() 后立即加载的依赖项

<script>
    var require = {
        deps: ["some/module1", "my/module2", "a.js", "b.js"],
        callback: function(module1, module2) {
            //This function will be called when all the dependencies
            //listed above in deps are loaded. Note that this
            //function could be called before the page is loaded.
            //This callback is optional.
        }
    };
</script>
<script src="scripts/require.js"></script>

注意: 最好用 var require = {} 并且不要用 window.require = {},它在 IE 中的行为不正确。

有一些 将配置与主模块加载分离的模式

支持的配置选项

baseUrl:用于所有模块查找的根路径。 因此,在上面的示例中,“my/module”的脚本标签将具有 src="/another/path/my/module.js"。 加载普通 .js 文件时使用 baseUrl(由以斜杠开头、具有协议或以 .js 结尾的依赖项字符串表示),这些字符串按原样使用,因此 a.js 和 b.js 将从包含上述代码段的 HTML 页面所在的同一目录加载。

如果未在配置中显式设置 baseUrl,则默认值将是加载 require.js 的 HTML 页面的位置。 如果使用了 data-main 属性,则该路径将成为 baseUrl。

baseUrl 可以是与加载 require.js 的页面不同的域上的 URL。RequireJS 脚本加载可以跨域工作。唯一的限制是 text! 插件加载的文本内容:这些路径应该与页面位于同一个域中,至少在开发过程中是这样。优化工具会将 text! 插件资源内联,因此在使用优化工具之后,您可以使用引用来自另一个域的 text! 插件资源的资源。

paths:baseUrl 下找不到的模块名称的路径映射。路径设置假定为相对于 baseUrl,除非路径设置以“/”开头或包含 URL 协议(“如 http:”)。使用上面的示例配置,“some/module”的脚本标签将是 src="/another/path/some/v1.0/module.js”。

用于模块名称的路径不应包含扩展名,因为路径映射可以指向目录。路径映射代码在将模块名称映射到路径时会自动添加 .js 扩展名。如果使用 require.toUrl(),它将添加适当的扩展名(如果它用于类似文本模板的内容)。

在浏览器中运行时,可以指定 paths 回退,以允许尝试从 CDN 位置加载,但如果 CDN 位置加载失败,则回退到本地位置。

bundles:在 RequireJS 2.1.10 中引入:允许将多个模块 ID 配置为在另一个脚本中找到。示例

requirejs.config({
    bundles: {
        'primary': ['main', 'util', 'text', 'text!template.html'],
        'secondary': ['text!secondary.html']
    }
});

require(['util', 'text'], function(util, text) {
    //The script for module ID 'primary' was loaded,
    //and that script included the define()'d
    //modules for 'util' and 'text'
});

该配置表明:模块“main”、“util”、“text”和“text!template.html”将通过加载模块 ID“primary”找到。模块“text!secondary.html”可以通过加载模块 ID“secondary”找到。

这仅设置在包含多个 define() 模块的脚本中查找模块的位置。它不会自动将这些模块绑定到捆绑包的模块 ID。捆绑包的模块 ID 仅用于定位模块集。

使用 paths 配置可以实现类似的功能,但它更加冗长,并且 paths 配置路由不允许在其配置中使用加载器插件资源 ID,因为 paths 配置值是路径段,而不是 ID。

如果正在执行构建并且该构建目标不是现有的模块 ID,或者如果您在构建的 JS 文件中具有不应由加载器插件加载的加载器插件资源,则 bundles 配置很有用。请注意,键和值是模块 ID,而不是路径段。它们是绝对模块 ID,而不是像 paths 配置map 配置 那样的模块 ID 前缀。此外,bundle 配置与 map 配置的不同之处在于,map 配置是一对一的模块 ID 关系,而 bundle 配置用于将多个模块 ID 指向一个捆绑包的模块 ID。

从 RequireJS 2.2.0 开始,优化器可以生成 bundles 配置并将其插入到顶层 requirejs.config() 调用中。有关更多详细信息,请参阅 bundlesConfigOutFile 构建配置选项。

shim:为不使用 define() 声明依赖项和设置模块值的旧式传统“浏览器全局变量”脚本配置依赖项、导出和自定义初始化。

这是一个示例。它需要 RequireJS 2.1.0+,并假定 backbone.js、underscore.js 和 jquery.js 已安装在 baseUrl 目录中。如果不是,则您可能需要为它们设置 paths 配置

requirejs.config({
    //Remember: only use shim config for non-AMD scripts,
    //scripts that do not already call define(). The shim
    //config will not work correctly if used on AMD scripts,
    //in particular, the exports and init config will not
    //be triggered, and the deps config will be confusing
    //for those cases.
    shim: {
        'backbone': {
            //These script dependencies should be loaded before loading
            //backbone.js
            deps: ['underscore', 'jquery'],
            //Once loaded, use the global 'Backbone' as the
            //module value.
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        },
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {
                //Using a function allows you to call noConflict for
                //libraries that support it, and do other cleanup.
                //However, plugins for those libraries may still want
                //a global. "this" for the function will be the global
                //object. The dependencies will be passed in as
                //function arguments. If this function returns a value,
                //then that value is used as the module export value
                //instead of the object found via the 'exports' string.
                //Note: jQuery registers as an AMD module via define(),
                //so this will not work for jQuery. See notes section
                //below for an approach for jQuery.
                return this.Foo.noConflict();
            }
        }
    }
});

//Then, later in a separate file, call it 'MyModel.js', a module is
//defined, specifying 'backbone' as a dependency. RequireJS will use
//the shim config to properly load 'backbone' and give a local
//reference to this module. The global Backbone will still exist on
//the page too.
define(['backbone'], function (Backbone) {
  return Backbone.Model.extend({});
});

在 RequireJS 2.0.* 中,shim 配置中的“exports”属性可以是函数而不是字符串。在这种情况下,它的功能与上面显示的“init”属性相同。RequireJS 2.1.0+ 中使用“init”模式,因此 exports 的字符串值可用于 enforceDefine,但在知道库已加载后允许函数工作。

对于仅是 jQuery 或 Backbone 插件且不需要导出任何模块值的“模块”,shim 配置可以只是一个依赖项数组

requirejs.config({
    shim: {
        'jquery.colorize': ['jquery'],
        'jquery.scroll': ['jquery'],
        'backbone.layoutmanager': ['backbone']
    }
});

但是请注意,如果您想在 IE 中获得 404 加载检测,以便您可以使用路径回退或错误回调,则应提供字符串导出值,以便加载器可以检查脚本是否实际加载(init 的返回值用于 enforceDefine 检查)

requirejs.config({
    shim: {
        'jquery.colorize': {
            deps: ['jquery'],
            exports: 'jQuery.fn.colorize'
        },
        'jquery.scroll': {
            deps: ['jquery'],
            exports: 'jQuery.fn.scroll'
        },
        'backbone.layoutmanager': {
            deps: ['backbone']
            exports: 'Backbone.LayoutManager'
        }
    }
});

“shim”配置的重要说明

  • shim 配置仅设置代码关系。要加载属于 shim 配置的一部分或使用 shim 配置的模块,需要正常的 require/define 调用。设置 shim 本身不会触发代码加载。
  • 仅将其他“shim”模块用作 shimmed 脚本的依赖项,或使用没有依赖项并在创建全局变量(如 jQuery 或 lodash)后调用 define() 的 AMD 库。否则,如果您使用 AMD 模块作为 shim 配置模块的依赖项,则在构建之后,该 AMD 模块可能要等到构建中的 shimmed 代码执行后才会被评估,并且会发生错误。最终的解决方法是升级所有 shimmed 代码以具有可选的 AMD define() 调用。
  • 如果无法升级 shimmed 代码以使用 AMD define() 调用,则从 RequireJS 2.1.11 开始,优化器具有 wrapShim 构建选项,该选项将尝试自动将 shimmed 代码包装在 define() 中以进行构建。这会更改 shimmed 依赖项的范围,因此不能保证始终有效,但是,例如,对于依赖于 AMD 版本 Backbone 的 shimmed 依赖项,它可能会有所帮助。
  • 不会为 AMD 模块调用 init 函数。例如,您不能使用 shim init 函数来调用 jQuery 的 noConflict。有关 jQuery 的替代方法,请参阅 映射模块以使用 noConflict
  • 通过 RequireJS 在节点中运行 AMD 模块时不支持 Shim 配置(尽管它适用于优化器使用)。根据被 shimmed 的模块,它在 Node 中可能会失败,因为 Node 没有与浏览器相同的全局环境。从 RequireJS 2.1.7 开始,它会在控制台中警告您不支持 shim 配置,并且它可能会也可能不会起作用。如果您希望抑制该消息,则可以传递 requirejs.config({ suppress: { nodeShim: true }});

“shim”配置的重要优化器说明:

  • 您应该使用 mainConfigFile 构建选项 来指定在其中找到 shim 配置的文件。否则,优化器将不知道 shim 配置。另一种选择是在构建配置文件中复制 shim 配置。
  • 不要在构建中将 CDN 加载与 shim 配置混合使用。示例场景:您从 CDN 加载 jQuery,但使用 shim 配置加载依赖于 jQuery 的旧版本 Backbone 之类的库。当您进行构建时,请确保在构建的文件中内联 jQuery,并且不要从 CDN 加载它。否则,Backbone 将在构建的文件中内联,并且会在 CDN 加载的 jQuery 加载之前执行。这是因为 shim 配置只是延迟文件的加载,直到加载了依赖项,但不会对 define 进行任何自动包装。构建之后,依赖项已经内联,shim 配置无法延迟非 define() 代码的执行。define() 模块在构建后可以使用 CDN 加载的代码,因为它们将源代码正确包装在 define 工厂函数中,该函数在加载依赖项之前不会执行。所以教训是:shim 配置是一种针对非模块化代码、遗留代码的权宜之计。define() 模块更好。
  • 对于本地多文件构建,上述 CDN 建议也适用。对于任何 shimmed 脚本,其依赖项必须在其执行之前加载。这意味着要么直接在包含 shimmed 脚本的构建层中构建其依赖项,要么使用 require([], function (){}) 调用加载其依赖项,然后对包含 shimmed 脚本的构建层进行嵌套 require([]) 调用。
  • 如果您正在使用 uglifyjs 来缩小代码,请勿将 uglify 选项 toplevel 设置为 true,或者如果使用命令行,请勿传递 -mt。该选项会破坏 shim 用于查找导出的全局名称。

map:对于给定的模块前缀,而不是使用给定的 ID 加载模块,而是替换为不同的模块 ID。

这种功能对于可能具有两组模块的大型项目非常重要,这两组模块需要使用两个不同版本的“foo”,但它们仍然需要相互协作。

上下文支持的多版本支持 无法实现这一点。此外,paths 配置 仅用于设置模块 ID 的根路径,而不用于将一个模块 ID 映射到另一个模块 ID。

map 示例

requirejs.config({
    map: {
        'some/newmodule': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});

如果模块在磁盘上的布局如下

  • foo1.0.js
  • foo1.2.js
  • some/
    • newmodule.js
    • oldmodule.js

当“some/newmodule”执行 `require('foo')` 时,它将获取 foo1.2.js 文件,而当“some/oldmodule”执行 `require('foo')` 时,它将获取 foo1.0.js 文件。

此功能仅适用于调用 define() 并注册为匿名模块的真实 AMD 模块的脚本。此外,仅对 map 配置使用绝对模块 ID。相对 ID(如 '../some/thing')不起作用。

还支持“*”map 值,这意味着“对于加载的所有模块,都使用此 map 配置”。如果有更具体的 map 配置,则该配置将优先于星号配置。示例


requirejs.config({
    map: {
        '*': {
            'foo': 'foo1.2'
        },
        'some/oldmodule': {
            'foo': 'foo1.0'
        }
    }
});

这意味着对于除“some/oldmodule”之外的任何模块,当需要“foo”时,请改用“foo1.2”。仅对于“some/oldmodule”,当它请求“foo”时使用“foo1.0”。

注意:在使用 map 配置进行构建时,需要将 map 配置提供给优化器,并且构建输出必须仍然包含设置 map 配置的 requirejs config 调用。优化器在构建期间不会执行 ID 重命名,因为项目中的某些依赖项引用可能取决于运行时变量状态。因此,优化器不会在构建后使 map 配置失效。

config:通常需要将配置信息传递给模块。该配置信息通常作为应用程序的一部分是已知的,并且需要一种方法将其传递给模块。在 RequireJS 中,这是通过 requirejs.config() 的 config 选项完成的。然后,模块可以通过请求特殊依赖项“module”并调用 module.config() 来读取该信息。示例

requirejs.config({
    config: {
        'bar': {
            size: 'large'
        },
        'baz': {
            color: 'blue'
        }
    }
});

//bar.js, which uses simplified CJS wrapping:
//http://requirejs.node.org.cn/docs/whyamd.html#sugar
define(function (require, exports, module) {
    //Will be the value 'large'
    var size = module.config().size;
});

//baz.js which uses a dependency array,
//it asks for the special module ID, 'module':
//https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#wiki-magic
define(['module'], function (module) {
    //Will be the value 'blue'
    var color = module.config().color;
});

要将配置传递给 ,请定位包中的主模块,而不是包 ID

requirejs.config({
    //Pass an API key for use in the pixie package's
    //main module.
    config: {
        'pixie/index': {
            apiKey: 'XJKDLNS'
        }
    },
    //Set up config for the "pixie" package, whose main
    //module is the index.js file in the pixie folder.
    packages: [
        {
            name: 'pixie',
            main: 'index'
        }
    ]
});

packages:配置从 CommonJS 包加载模块。有关更多信息,请参阅 包主题

nodeIdCompat:Node 将模块 ID example.jsexample 视为相同。默认情况下,这些是 RequireJS 中的两个不同 ID。如果您最终使用从 npm 安装的模块,则可能需要将此配置值设置为 true 以避免解析问题。此选项仅适用于以不同方式处理“.js”后缀,它不会执行任何其他节点解析和评估匹配,例如 .json 文件处理(JSON 处理无论如何都需要“json!”加载器插件)。在 2.1.10 及更高版本中可用。

waitSeconds:放弃加载脚本之前的等待秒数。将其设置为 0 将禁用超时。默认值为 7 秒。

context:要赋予加载上下文的名称。只要每个顶级 require 调用都指定了唯一的上下文字符串,这将允许 require.js 在页面中加载多个版本的模块。要正确使用它,请参阅 多版本支持 部分。

deps:要加载的依赖项数组。在加载 require.js 之前将 require 定义为配置对象时非常有用,并且您希望指定在定义 require() 后立即加载的依赖项。使用 deps 就像执行 require([]) 调用一样,但在加载器处理完配置后立即完成。它不会阻止任何其他 require() 调用开始请求模块,它只是一种指定要作为配置块的一部分异步加载的某些模块的方法。

callback: 在加载 deps 后执行的函数。当在加载 require.js 之前将 require 定义为配置对象,并且您想在配置的 deps 数组加载后指定要加载的函数时非常有用。

enforceDefine: 如果设置为 true,则在加载未调用 define() 或没有可检查的 shim 导出字符串值的脚本时,将引发错误。有关详细信息,请参阅在 IE 中捕获加载失败

xhtml: 如果设置为 true,则将使用 document.createElementNS() 创建脚本元素。

urlArgs: 附加到 RequireJS 用于获取资源的 URL 的额外查询字符串参数。当浏览器或服务器配置不正确时,这对于缓存清除最有用。urlArgs 的示例缓存清除设置

urlArgs: "bust=" +  (new Date()).getTime()

从 RequireJS 2.2.0 开始,urlArgs 可以是一个函数。如果是函数,它将接收模块 ID 和 URL 作为参数,并且它应该返回一个字符串,该字符串将添加到 URL 的末尾。如果没有参数,则返回空字符串。请务必根据 URL 的现有状态添加“?”或“&”。示例

requirejs.config({
    urlArgs: function(id, url) {
        var args = 'v=1';
        if (url.indexOf('view.html') !== -1) {
            args = 'v=2'
        }

        return (url.indexOf('?') === -1 ? '?' : '&') + args;
    }
});

在开发过程中,使用它可能很有用,但是请务必在部署代码之前将其删除。

scriptType: 指定用于 RequireJS 插入到文档中的脚本标签的 type="" 属性的值。默认为“text/javascript”。要使用 Firefox 的 JavaScript 1.8 功能,请使用“text/javascript;version=1.8”。

skipDataMain: 在 RequireJS 2.1.9 中引入:如果设置为 true,则跳过为启动模块加载而执行的 data-main 属性扫描。如果 RequireJS 嵌入到可能与页面上的其他 RequireJS 库交互的实用程序库中,并且嵌入式版本不应执行 data-main 加载,则此选项很有用。

高级用法 § 4

从包加载模块§ 4.1

RequireJS 支持加载位于 CommonJS 包 目录结构中的模块,但需要指定一些额外的配置才能使其正常工作。具体来说,支持以下 CommonJS 包功能

  • 包可以与模块名称/前缀相关联。
  • 包配置可以为特定包指定以下属性
    • name: 包的名称(用于模块名称/前缀映射)
    • location: 磁盘上的位置。位置是相对于 baseUrl 配置值的,除非它们包含协议或以正斜杠 (/) 开头。
    • main: 当有人需要“packageName”时,应使用包内模块的名称。默认值为“main”,因此仅当它与默认值不同时才指定它。该值是相对于包文件夹的。

重要说明

  • 虽然包可以具有 CommonJS 目录布局,但模块本身应该采用 RequireJS 可以理解的模块格式。规则的例外:如果您使用的是 r.js Node 适配器,则模块可以采用传统的 CommonJS 模块格式。如果需要将传统的 CommonJS 模块转换为 RequireJS 使用的异步模块格式,可以使用 CommonJS 转换器工具
  • 在项目上下文中一次只能使用一个版本的包。您可以使用 RequireJS 多版本支持 加载两个不同的模块上下文,但是如果您想在一个上下文中使用包 A 和 B,并且它们依赖于不同版本的包 C,那么这将是一个问题。这在将来可能会改变。

如果您使用与 入门指南 中指定的类似的项目布局,则 Web 项目的开头将如下所示(基于 Node/Rhino 的项目类似,只需使用 scripts 目录的内容作为顶级项目目录)

  • project-directory/
    • project.html
    • scripts/
      • require.js

以下是包含两个包 cartstore 的示例目录布局

  • project-directory/
    • project.html
    • scripts/
      • cart/
        • main.js
      • store/
        • main.js
        • util.js
      • main.js
      • require.js

project.html 将具有如下所示的脚本标签

<script data-main="scripts/main" src="scripts/require.js"></script>

这将指示 require.js 加载 scripts/main.js。main.js 使用“packages”配置来设置相对于 require.js 的包,在本例中为源包“cart”和“store”

//main.js contents
//Pass a config object to require
require.config({
    "packages": ["cart", "store"]
});

require(["cart", "store", "store/util"],
function (cart,   store,   util) {
    //use the modules as usual.
});

需要“cart”意味着它将从 scripts/cart/main.js 加载,因为“main”是 RequireJS 支持的默认主模块设置。需要“store/util”将从 scripts/store/util.js 加载。

如果“store”包不遵循“main.js”约定,并且看起来更像这样

  • project-directory/
    • project.html
    • scripts/
      • cart/
        • main.js
      • store/
        • store.js
        • util.js
      • main.js
      • package.json
      • require.js

那么 RequireJS 配置将如下所示

require.config({
    packages: [
        "cart",
        {
            name: "store",
            main: "store"
        }
    ]
});

为了避免冗长,强烈建议始终使用在其结构中使用“main”约定的包。

多版本支持§ 4.2

配置选项中所述,可以通过使用不同的“context”配置选项在页面中加载模块的多个版本。require.config() 返回一个 require 函数,该函数将使用上下文配置。以下示例加载了 alpha 和 beta 模块的两个不同版本(此示例取自其中一个测试文件)

<script src="../require.js"></script>
<script>
var reqOne = require.config({
  context: "version1",
  baseUrl: "version1"
});

reqOne(["require", "alpha", "beta",],
function(require,   alpha,   beta) {
  log("alpha version is: " + alpha.version); //prints 1
  log("beta version is: " + beta.version); //prints 1

  setTimeout(function() {
    require(["omega"],
      function(omega) {
        log("version1 omega loaded with version: " +
             omega.version); //prints 1
      }
    );
  }, 100);
});

var reqTwo = require.config({
      context: "version2",
      baseUrl: "version2"
    });

reqTwo(["require", "alpha", "beta"],
function(require,   alpha,   beta) {
  log("alpha version is: " + alpha.version); //prints 2
  log("beta version is: " + beta.version); //prints 2

  setTimeout(function() {
    require(["omega"],
      function(omega) {
        log("version2 omega loaded with version: " +
            omega.version); //prints 2
      }
    );
  }, 100);
});
</script>

请注意,“require”被指定为模块的依赖项。这允许传递给函数回调的 require() 函数使用正确的上下文加载模块,以实现多版本支持。如果未将“require”指定为依赖项,则可能会出现错误。

页面加载后加载代码§ 4.3

多版本支持部分中的上述示例显示了如何通过嵌套的 require() 调用稍后加载代码。

Web Worker 支持§ 4.4

从 0.12 版本开始,RequireJS 可以在 Web Worker 中运行。只需在 Web Worker 中使用 importScripts() 加载 require.js(或包含 require() 定义的 JS 文件),然后调用 require 即可。

您可能需要设置 baseUrl 配置选项,以确保 require() 可以找到要加载的脚本。

您可以通过查看 单元测试 中使用的一个文件来查看其用法示例。

Rhino 支持§ 4.5

RequireJS 可以通过 r.js 适配器 在 Rhino 中使用。有关详细信息,请参阅 r.js 自述文件

Nashorn 支持§ 4.6

从 RequireJS 2.1.16 开始,RequireJS 可以通过 r.js 适配器 在 Java 8+ 的 JavaScript 引擎 Nashorn 中使用。有关详细信息,请参阅 r.js 自述文件

处理错误§ 4.7

一般错误类型是脚本的 404 错误(未找到)、网络超时或加载的脚本中的错误。RequireJS 提供了一些工具来处理它们:特定于 require 的错误回调、“paths”数组配置和全局 requirejs.onError。

传递给错误回调和全局 requirejs.onError 函数的错误对象通常包含两个自定义属性

  • requireType: 具有常规分类的字符串值,例如“timeout”、“nodefine”、“scripterror”。
  • requireModules: 超时的模块名称/URL 数组。

如果收到带有 requireModules 的错误,则可能意味着依赖于该 requireModules 数组中的模块的其他模块未定义。

在 IE 中捕获加载失败 § 4.6.1

Internet Explorer 有一系列问题,使得很难检测错误回调/路径回退的加载失败

  • script.onerror 在 IE 6-8 中不起作用。无法知道加载脚本是否生成 404 错误,更糟糕的是,即使在 404 错误情况下,它也会触发 onreadystatechange 并将其状态设置为完成。
  • script.onerror 在 IE 9+ 中起作用,但它有一个错误,即它不会在执行脚本后立即触发 script.onload 事件处理程序,因此它不支持允许匿名 AMD 模块的标准方法。所以仍然使用 script.onreadystatechange。但是,onreadystatechange 会在 script.onerror 函数触发之前触发,并将其状态设置为完成。

因此,使用 IE 很难同时允许匿名 AMD 模块(这是 AMD 模块的核心优势)和可靠地检测错误。

但是,如果您在一个项目中,并且您知道该项目使用 define() 来声明其所有模块,或者它使用 shim 配置为任何不使用 define() 的内容指定字符串导出,那么如果您将 enforceDefine 配置值设置为 true,则加载程序可以通过检查 define() 调用或 shim 的导出全局值的存在来确认脚本是否已加载。

因此,如果您想支持 Internet Explorer、捕获加载错误并通过直接 define() 调用或 shim 配置获得模块化代码,请始终将 enforceDefine 设置为 true。有关示例,请参见下一节。

注意:如果您确实设置了 enforceDefine: true,并且使用 data-main="" 加载主 JS 模块,则该主 JS 模块必须调用 define() 而不是 require() 来加载它需要的代码。主 JS 模块仍然可以调用 require/requirejs 来设置配置值,但是对于加载模块,它应该使用 define()。

如果您还使用 almond 在没有 require.js 的情况下构建代码,请务必使用 insertRequire 构建设置来插入对主模块的 require 调用——这与 data-main 所做的初始 require() 调用具有相同的目的。

require([]) 错误回调 § 4.6.2

requirejs.undef() 一起使用时,错误回调将允许您检测模块是否加载失败、取消定义该模块、将配置重置到另一个位置,然后重试。

这方面的一个常见用例是使用库的 CDN 托管版本,但如果失败,则切换到本地加载文件

requirejs.config({
    enforceDefine: true,
    paths: {
        jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'
    }
});

//Later
require(['jquery'], function ($) {
    //Do something with $ here
}, function (err) {
    //The errback, error callback
    //The error has a list of modules that failed
    var failedId = err.requireModules && err.requireModules[0];
    if (failedId === 'jquery') {
        //undef is function only on the global requirejs object.
        //Use it to clear internal knowledge of jQuery. Any modules
        //that were dependent on jQuery and in the middle of loading
        //will not be loaded yet, they will wait until a valid jQuery
        //does load.
        requirejs.undef(failedId);

        //Set the path to jQuery to local path
        requirejs.config({
            paths: {
                jquery: 'local/jquery'
            }
        });

        //Try again. Note that the above require callback
        //with the "Do something with $ here" comment will
        //be called if this new attempt to load jQuery succeeds.
        require(['jquery'], function () {});
    } else {
        //Some other error. Maybe show message to the user.
    }
});

使用 `requirejs.undef()`,如果您稍后设置了不同的配置并尝试加载相同的模块,则加载程序仍会记住哪些模块需要该依赖项,并在新配置的模块加载时完成加载它们。

注意:错误回调仅适用于回调样式的 require 调用,不适用于 define() 调用。define() 仅用于声明模块。

paths 配置回退 § 4.6.3

上述用于检测加载失败、取消定义模块、修改路径和重新加载的模式是一个非常常见的请求,因此还有一个简写形式。paths 配置允许数组值

requirejs.config({
    //To get timely, correct error triggers in IE, force a define/shim exports check.
    enforceDefine: true,
    paths: {
        jquery: [
            'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min',
            //If the CDN location fails, load from this location
            'lib/jquery'
        ]
    }
});

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

上面的代码将尝试 CDN 位置,但如果失败,则回退到本地 lib/jquery.js 位置。

注意:路径回退仅适用于完全匹配的模块 ID。这与可以应用于模块 ID 前缀段的任何部分的普通路径配置不同。回退更侧重于异常错误恢复,而不是通用的路径搜索路径解决方案,因为它们在浏览器中效率低下。

全局 requirejs.onError 函数 § 4.6.4

要检测未被本地错误回调捕获的错误,可以覆盖 requirejs.onError()

requirejs.onError = function (err) {
    console.log(err.requireType);
    if (err.requireType === 'timeout') {
        console.log('modules: ' + err.requireModules);
    }

    throw err;
};

加载器插件 § 5

RequireJS 支持 加载器插件。这是一种支持不是普通 JS 文件但对于脚本在执行其工作之前加载仍然很重要的依赖项的方法。RequireJS wiki 上有一个 插件列表。本节讨论与 RequireJS 一起维护的一些特定插件

指定文本文件依赖项§ 5.1

使用常规 HTML 标签构建 HTML 非常方便,而不必在脚本中构建 DOM 结构。但是,没有很好的方法将 HTML 嵌入到 JavaScript 文件中。最好的办法是使用 HTML 字符串,但这可能难以管理,特别是对于多行 HTML。

RequireJS 有一个插件 text.js,可以帮助解决这个问题。如果为依赖项使用了 text! 前缀,它将自动加载。有关更多信息,请参阅 text.js README

页面加载事件支持/DOM 就绪§ 5.2

使用 RequireJS 时,可能会遇到脚本加载速度过快,以至于在 DOM 就绪之前就已完成的情况。任何试图与 DOM 交互的操作都应该等待 DOM 就绪。对于现代浏览器,这是通过等待 DOMContentLoaded 事件来完成的。

但是,并非所有正在使用的浏览器都支持 DOMContentLoaded。domReady 模块实现了一种跨浏览器方法来确定 DOM 何时就绪。下载模块并在您的项目中像这样使用它

require(['domReady'], function (domReady) {
  domReady(function () {
    //This function is called once the DOM is ready.
    //It will be safe to query the DOM and manipulate
    //DOM nodes in this function.
  });
});

由于 DOM 就绪是一个常见的应用程序需求,理想情况下,可以避免上述 API 中的嵌套函数。domReady 模块还实现了 加载器插件 API,因此您可以使用加载器插件语法(注意 domReady 依赖项中的 !)来强制 require() 回调函数在执行之前等待 DOM 就绪。

domReady
当用作加载器插件时,将返回当前文档

require(['domReady!'], function (doc) {
    //This function is called once the DOM is ready,
    //notice the value for 'domReady!' is the current
    //document.
});

注意:如果文档加载时间过长(可能是文档非常大,或者有 HTML 脚本标签加载了阻止 DOM 完成的大型 JS 文件,直到它们完成),则使用 domReady 作为加载器插件可能会导致 RequireJS“超时”错误。如果这是一个问题,请增加 waitSeconds 配置,或者将 domReady 用作模块并在 require() 回调中调用 domReady()。

定义 I18N 包§ 5.3

一旦您的 Web 应用程序达到一定规模和流行度,本地化界面中的字符串并提供其他特定于区域设置的信息就变得更加有用了。但是,要制定一个能够很好地扩展以支持多种区域设置的方案可能会很麻烦。

RequireJS 允许您设置一个包含本地化信息的基本模块,而无需您预先提供所有特定于区域设置的信息。它可以随着时间的推移而添加,并且只能在特定于区域设置的文件中定义在区域设置之间更改的字符串/值。

i18n.js 插件提供了 i18n 包支持。当模块或依赖项指定 i18n! 前缀时,它会自动加载(更多信息如下)。下载插件并将其放在与应用程序的主 JS 文件相同的目录中。

要定义包,请将其放在名为“nls”的目录中 - i18n! 插件假定名称中带有“nls”的模块表示 i18n 包。名称中的“nls”标记告诉 i18n 插件在哪里可以找到区域设置目录(它们应该是 nls 目录的直接子目录)。如果您想在“my”模块集中提供颜色名称包,请创建如下目录结构

  • my/nls/colors.js

该文件的内容应如下所示

//my/nls/colors.js contents:
define({
    "root": {
        "red": "red",
        "blue": "blue",
        "green": "green"
    }
});

具有“root”属性的对象字面量定义了此模块。这就是您为以后的本地化工作做好准备所需要做的全部工作。

然后,您可以在另一个模块中使用上述模块,例如,在 my/lamps.js 文件中

//Contents of my/lamps.js
define(["i18n!my/nls/colors"], function(colors) {
    return {
        testMessage: "The name for red in this locale is: " + colors.red
    }
});

my/lamps 模块有一个名为“testMessage”的属性,它使用 colors.red 来显示颜色红色的本地化值。

稍后,当您想向文件添加特定翻译时,例如针对 fr-fr 区域设置,请将 my/nls/colors 更改为如下所示

//Contents of my/nls/colors.js
define({
    "root": {
        "red": "red",
        "blue": "blue",
        "green": "green"
    },
    "fr-fr": true
});

然后在 my/nls/fr-fr/colors.js 中定义一个具有以下内容的文件

//Contents of my/nls/fr-fr/colors.js
define({
    "red": "rouge",
    "blue": "bleu",
    "green": "vert"
});

RequireJS 将使用浏览器的 navigator.languages、navigator.language 或 navigator.userLanguage 属性来确定要为 my/nls/colors 使用哪些区域设置值,因此您的应用程序不必更改。如果您更喜欢设置区域设置,则可以使用 模块配置将区域设置传递给插件

requirejs.config({
    config: {
        //Set the config for the i18n
        //module ID
        i18n: {
            locale: 'fr-fr'
        }
    }
});

注意,RequireJS 将始终使用区域设置的小写版本,以避免大小写问题,因此 i18n 包的所有目录和磁盘文件都应使用小写区域设置。

RequireJS 还足够智能,可以挑选正确的区域设置包,即与 my/nls/colors 提供的包最匹配的包。例如,如果区域设置为“en-us”,则将使用“root”包。如果区域设置为“fr-fr-paris”,则将使用“fr-fr”包。

RequireJS 还将包组合在一起,例如,如果法语包定义如下(省略红色的值)

//Contents of my/nls/fr-fr/colors.js
define({
    "blue": "bleu",
    "green": "vert"
});

然后将使用“root”中的红色值。这适用于所有区域设置部分。如果定义了下面列出的所有包,则 RequireJS 将按以下优先级顺序使用值(最上面的优先级最高)

  • my/nls/fr-fr-paris/colors.js
  • my/nls/fr-fr/colors.js
  • my/nls/fr/colors.js
  • my/nls/colors.js

如果您不想在顶级模块中包含根包,则可以像普通区域设置包一样定义它。在这种情况下,顶级模块将如下所示

//my/nls/colors.js contents:
define({
    "root": true,
    "fr-fr": true,
    "fr-fr-paris": true
});

根包将如下所示

//Contents of my/nls/root/colors.js
define({
    "red": "red",
    "blue": "blue",
    "green": "green"
});