RequireJS API
- 使用说明§§ 1-1.3
- 加载 JavaScript 文件§ 1.1
- data-main 入口点§ 1.2
- 调用中以进行配置。§ 1.3
- 简单的名称/值对§ 1.3.1
- 定义函数§ 1.3.2
- 带有依赖的定义函数§ 1.3.3
- 将模块定义为函数§ 1.3.4
- 使用简化版 CommonJS 包装器定义模块§ 1.3.5
- 定义带名称的模块§ 1.3.6
- 其他模块注意事项§ 1.3.7
- 循环依赖§ 1.3.8
- 指定 JSONP 服务依赖§ 1.3.9
- 取消定义模块§ 1.3.10
- 机制§§ 2
- 配置选项§§ 3
- 高级用法§§ 4-4.6
- 从包中加载模块§ 4.1
- 多版本支持§ 4.2
- 页面加载后加载代码§ 4.3
- Web Worker 支持§ 4.4
- Rhino 支持§ 4.5
- Nashorn 支持§ 4.6
- 错误处理§ 4.7
- 加载器插件§§ 5-5.4
- 指定文本文件依赖§ 5.1
- 页面加载事件支持 / DOM 就绪§ 5.2
- 定义一个国际化(I18N)资源包§ 5.3
使用说明 § 1
加载 JavaScript 文件 § 1.1
RequireJS 的脚本加载方法与传统的 <script> 标签不同。虽然它同样可以运行得很快并优化得很好,但其主要目标是鼓励编写模块化代码。作为该目标的一部分,它鼓励使用模块 ID而不是用于脚本标签的 URL。
RequireJS 相对于一个baseUrl来加载所有代码。baseUrl 通常设置为与页面顶层脚本所使用的 data-main 属性中的脚本所在的目录相同。data-main 属性是 require.js 用来检查以开始脚本加载的一个特殊属性。此示例最终将拥有一个 baseUrl 为scripts:
<!--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 具有以下任一特征,则不会通过 "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
- app/
在 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 脚本的加载和执行会在页面后续引用的其他脚本之前完成。
例如,当 'foo' 模块的 require.config 路径尚未设置时就调用 require() 加载它,下面这种安排会随机失败:
<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) {
});
如果你想在require()
调用require()
在 HTML 页面中,那么最好不要使用 data-main。data-main 仅用于页面只有一个主入口点(即 data-main 脚本)的情况。对于希望进行内联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 页面上找到,在Why 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.
});
如果这是 paths 配置:
require.config({
baseUrl: 'lib',
paths: {
'compute': 'compute/main'
}
});
并且一个require(['compute'])
操作完成,那么 lib/compute/main.js 将具有模块名为 'compute'。当它请求 './extras' 时,该路径是相对于 'compute' 解析的,所以变为 'compute/./extras',规范化后就是 'extras'。由于没有针对该模块名的 paths 配置,生成的路径将是 'lib/extras.js',这是不正确的。
对于这种情况,packages 配置是一个更好的选择,因为它允许将主模块设置为 'compute',但内部加载器会以 'compute/main' 的 ID 存储该模块,因此对 './extras' 的相对引用能够正常工作。
另一种方法是在 lib/compute.js 处构造一个模块,内容仅仅是define(['./compute/main'], function(m) { return m; });
,这样就不需要配置 paths 或 packages。
或者,不设置 paths 或 packages 配置,并将顶层的 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”的值将是 undefined。模块“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 中调用某些服务的方法。它能跨域工作,是一种成熟的通过 script 标签进行 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 服务时发生的错误通常表现为服务超时,因为 script 标签加载不会提供详细的网络问题信息。要检测错误,你可以重写 requirejs.onError() 来捕获错误。在错误处理章节。
取消定义模块§ 1.3.10
有一个全局函数,requirejs.undef(),它允许取消定义一个模块。它将重置加载器的内部状态,使其忘记该模块之前的定义。
然而,它不会从其他已定义并在执行时作为依赖项获得该模块的模块中移除该模块。因此,它实际上只在无法加载其他模块或者没有其它模块获取到该模块值的错误情况下有用,或者用于将来可能使用该模块的任何新模块加载。参见errback 章节获取一个示例。
如果你想进行更复杂的依赖图分析以完成 undefining 工作,半私有onResourceLoad API可能会有所帮助。
机制 § 2
RequireJS 使用 head.appendChild() 通过 script 标签加载每个依赖项。
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>
你也可以在你的data-main 入口点中调用 require.config,但请注意 data-main 脚本是异步加载的。应避免其他错误地假定 data-main 及其 require.config 总是在它们的脚本加载之前执行的入口点脚本。
此外,你可以将配置对象定义为全局变量require
在require.js 被加载之前,并自动应用这些值。此示例指定了一些依赖项,在 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"。baseUrl 是不要加载普通 .js 文件时使用的路径(由依赖字符串标识,以斜杠开头、带有协议或者以 .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()被使用,它会在适当时添加相应的扩展名,比如用于文本模板的情况。
当在浏览器中运行时,路径回退可以被指定,以便首先尝试从 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()'d 模块的脚本中找到特定模块。这并不会自动将这些模块绑定到该 bundle 的模块 ID 上。bundle 的模块 ID 只是用于定位一组模块。
使用 paths 配置也能实现类似效果,但这会更繁琐,并且 paths 配置的方式不允许在其配置中使用加载器插件资源 ID,因为 paths 的配置值是路径片段,而不是模块 ID。
bundles 配置在进行构建时很有用,特别是当你构建的目标不是一个已有的模块 ID,或者如果你已经打包好的 JS 文件中包含某些不应该通过加载器插件加载的加载器插件资源时。注意 bundles 配置中的键和值都是模块 ID,而不是路径片段。它们是绝对模块 ID,而不是像paths配置中的map 配置这样的模块 ID 前缀。此外,bundle 配置不同于 map 配置,map 配置是一对一的模块 ID 映射,而 bundle 配置则是将多个模块 ID 指向一个 bundle 的模块 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 加载检测功能以便使用路径回退或错误回调,则应提供一个字符串形式的 exports 值,这样加载器才能检查脚本是否真的加载了(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 配置的模块,仍需要正常的 require/define 调用。仅设置 shim 本身不会触发代码加载。
- 仅为 shim 脚本的依赖项使用其他“shim”模块,或者使用那些没有依赖项并在创建全局变量(如 jQuery 或 lodash)之后调用 define() 的 AMD 库。否则,如果你在 shim 配置模块中使用 AMD 模块作为依赖项,在构建之后,该 AMD 模块可能直到 shim 代码执行完毕后才会被求值,从而导致错误。最终的解决方法是升级所有 shim 代码以支持可选的 AMD define() 调用。
- 如果无法将 shim 代码升级为使用 AMD define() 调用,则从 RequireJS 2.1.11 开始,优化器有一个wrapShim 构建选项它会尝试自动为构建中的 shim 代码包裹上 define()。这将改变 shim 依赖的作用域,因此不能保证总是有效,但在某些情况下有用,比如 shim 依赖依赖于某个 AMD 版本的 Backbone 时。
- init 函数将不要被用于 AMD 模块。例如,你不能使用 shim 的 init 函数来调用 jQuery 的 noConflict。请参见映射模块以使用 noConflict有关 jQuery 的替代方法。
- 在通过 RequireJS 在 Node 中运行 AMD 模块时,不支持 Shim 配置(但优化器使用时仍有效)。根据所 shim 的模块不同,在 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()'d 代码的执行。define()'d 的模块可以在构建后与 CDN 加载的代码一起正常工作,因为它们正确地将自己的源码封装在 define 工厂函数内,只有当依赖项加载完成之后才会执行。所以结论是:shim 配置只是临时解决方案,用于非模块化代码、遗留代码。define()'d 模块更好。
- 对于本地多文件构建,上述关于 CDN 的建议同样适用。对于任何 shim 脚本,其依赖项必须在 shim 脚本执行之前加载。这意味着要么将其依赖项直接构建进包含 shim 脚本的构建层中,要么先通过
require([], function (){})
调用来加载它的依赖,然后在包含 shim 脚本的构建层中执行嵌套的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 文件。
此功能仅对实际的 AMD 模块(调用 define() 并注册为匿名模块的脚本)有效。另外,请只对 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 配置提供给优化器,并且构建输出仍然必须包含一个 requirejs 配置调用以设置 map 配置。优化器在构建期间不会重命名 ID,因为项目中某些依赖引用可能依赖于运行时的变量状态。因此,优化器不会使构建之后的 map 配置失效。
config通常有一个常见的需求,就是向模块传递配置信息。这种配置信息通常是作为应用程序的一部分已知的,需要一种方法将它传递到模块里。在 RequireJS 中是通过configrequirejs.config() 的选项实现的。模块可以通过请求特殊的依赖 "module" 并调用module.config()来读取该信息。示例:
requirejs.config({
config: {
'bar': {
size: 'large'
},
'baz': {
color: 'blue'
}
}
});
//bar.js, which uses simplified CJS wrapping:
//https://requirejs.org/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;
});
为将配置传递给一个package(包),请指向包中的 main 模块,而不是包 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 包加载模块。参见packages 主题了解更多详情。
nodeIdCompat: Node 将模块 ID 视为example.js
和example
相同的处理方式。默认情况下,在 RequireJS 中这两个是不同的 ID。如果你最终使用了从 npm 安装的模块,你可能需要将此配置值设为true
以避免解析问题。此选项只影响 ".js" 后缀的处理方式不同,不会做其他类似 node 的解析和评估匹配,例如 .json 文件的处理(JSON 处理无论如何都需要 'json!' 加载插件)。该功能在 RequireJS 2.1.10 及以上版本可用。
waitSeconds: 在放弃加载脚本之前等待的秒数。将其设为 0 可禁用超时。默认是 7 秒。
context: 给加载上下文命名。这允许 require.js 在页面中加载多个版本的模块,只要每个顶层的 require 调用指定了唯一的 context 字符串即可。要正确使用它,请参考多版本支持章节。
deps: 一个要加载的依赖数组。这在 require 被定义为配置对象时很有用,此时 require.js 尚未加载,而你希望在 require() 定义后立即指定一些要加载的依赖项。使用 deps 就如同执行一次require([])
调用一样,它不会阻止其他 require() 调用开始它们对模块的请求,这只是在配置块中指定一些异步加载模块的方法。
callback: 在deps加载完成后执行的一个函数。这在 require 被定义为配置对象时很有用,此时 require.js 尚未加载,而你想要在配置的deps数组加载完成后指定一个 require 函数。
enforceDefine: 如果设为 true,当加载一个没有调用 define() 或者没有 shim 的 exports 字符串值可供检查的脚本时,会抛出错误。参见捕获 IE 中的加载失败了解更多详情。
xhtml: 如果设为 true,将会使用 document.createElementNS() 创建 script 元素。
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 插入文档中的 script 标签所使用的 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 包特性:
- 一个包可以和一个模块名/前缀关联。
- 包配置可以为特定的包指定以下属性:
- namename: 包的名称(用于模块名/前缀映射)
- location: 磁盘上的位置。位置相对于 baseUrl 配置值,除非它们包含协议或以正斜杠 (/) 开头。
- main: 当有人请求 "packageName" 时应该使用的包内模块名称。默认值为 "main",所以只有在不同于默认值时才需要指定。该值相对于包文件夹。
重要注意事项
- 虽然包可以具有 CommonJS 的目录结构,但模块本身应采用 RequireJS 可以理解的模块格式。例外情况:如果你正在使用 r.js 的 Node 适配器,则模块可以是传统的 CommonJS 模块格式。你可以在需要将传统 CommonJS 模块转换为 RequireJS 使用的异步模块格式时使用CommonJS 转换工具如果你需要将传统 CommonJS 模块转换为 RequireJS 所使用的异步模块格式。
- 在一个项目上下文中一次只能使用一个版本的包。你可以使用 RequireJS多版本支持来加载两个不同的模块上下文,但是如果你想在一个上下文中同时使用包 A 和 B,并且它们依赖于不同版本的包 C,这就会成为一个问题。未来可能会有所改变。
如果你使用的项目结构与以下内容中指定的类似:开始指南那么你的 Web 项目的开始可能看起来像这样(Node/Rhino 项目类似,只需使用scripts目录作为顶级项目目录):
- project-directory/
- project.html
- scripts/
- require.js
下面是包含两个包的示例目录结构,cart和store:
- project-directory/
- project.html
- scripts/
- cart/
- main.js
- store/
- main.js
- util.js
- main.js
- require.js
- cart/
project.html将会有如下 script 标签:
<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
- cart/
那么 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 适配器在r.js README了解更多详情。
Nashorn 支持§ 4.6
自 RequireJS 2.1.16 版本起,RequireJS 可以通过NashornJava 8+ 的 JavaScript 引擎中使用。请参阅r.js 适配器在r.js README了解更多详情。
错误处理§ 4.7
一般类别的错误包括脚本 404(未找到)、网络超时或加载的脚本中的错误。RequireJS 提供了几个工具来处理这些问题:require 专用的错误回调、"paths" 数组配置,以及全局的 requirejs.onError。
传递给 errbacks 和全局 requirejs.onError 函数的错误对象通常包含两个自定义属性:
- requireType: 一个字符串值,用于表示一般的分类,例如 "timeout"、"nodefine"、"scripterror"。
- requireModules: 超时的模块名称/URL 的数组。
如果你收到带有 requireModules 的错误,很可能意味着依赖该 requireModules 数组中模块的其他模块未被定义。
捕获 IE 中的加载失败 § 4.6.1
Internet Explorer 存在一些问题,使得难以检测加载失败导致的 errbacks/paths 回退:
- script.onerror 在 IE 6-8 中不起作用。无法知道加载脚本是否生成了 404 错误,更糟的是,即使出现 404,它仍然触发 onreadystatechange 事件并处于 complete 状态。
- script.onerror 在 IE 9+ 中可用,但它存在一个 bug,即不会在脚本执行后立即触发 script.onload 事件处理程序,因此无法支持允许匿名 AMD 模块的标准方法。所以仍需使用 script.onreadystatechange。但是,onreadystatechange 在 script.onerror 触发之前就先以 complete 状态被触发。
因此,在 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([]) 错误回调(errbacks) § 4.6.2
当 errbacks 与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()`,如果你稍后设置了不同的配置并尝试加载相同的模块,加载器仍将记住哪些模块需要该依赖,并在重新配置后的模块加载时完成它们的加载。
注意:errbacks 只适用于回调风格的 require 调用,不适用于 define() 调用。define() 仅用于声明模块。
paths 配置回退 § 4.6.3
上述用于检测加载失败、undef() 模块、修改路径并重新加载的模式足够常见,以至于也有一个简写方式。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 地址。
注意:paths 回退只对精确匹配的模块 ID 有效。这不同于普通的 paths 配置,后者可应用于模块 ID 前缀的任何部分。回退机制主要针对异常错误恢复而设计,而不是通用的路径搜索解决方案,因为那样在浏览器中效率较低。
全局 requirejs.onError 函数 § 4.6.4
要检测未被本地 errbacks 捕获的错误,你可以覆盖 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 的维基页面提供了插件列表。本节介绍了一些与 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.
});
注意:如果文档加载耗时较长(可能是文档体积很大,或者包含加载大型 JS 文件的 HTML script 标签,在加载完成前会阻塞 DOM 就绪),使用 domReady 作为加载器插件可能会导致 RequireJS 的“超时”错误。如果出现该问题,可以增加waitSeconds配置值,或直接将 domReady 作为模块使用并在 require() 回调中调用 domReady()。
定义一个国际化(I18N)资源包§ 5.3
一旦你的 Web 应用达到一定规模和受欢迎程度,将界面中的字符串本地化并提供其他本地相关信息就会变得更有用。然而,要想设计出一个能良好扩展以支持多个区域设置的方案可能会很麻烦。
RequireJS 允许你设置一个基础模块来包含本地化信息,而不必一开始就提供所有本地相关的信息。它可以逐步添加,并且只需在本地化文件中定义那些在不同区域之间变化的字符串/值。
i18n bundle 支持由 i18n.js 插件提供。当模块或依赖项指定了 i18n! 前缀时会自动加载该插件(更多信息见下文)。下载该插件并将其放在与你的应用程序主JS文件相同的目录中。
要定义一个资源包,请将其放在名为“nls”的目录中——i18n!插件假定模块名中带有“nls”表示一个国际化资源包。“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还会将资源包组合在一起,例如,如果法语资源包定义如下(省略了red的值):
//Contents of my/nls/fr-fr/colors.js
define({
"blue": "bleu",
"green": "vert"
});
那么将使用“root”资源包中的red值。这对于所有的区域设置部分同样有效。如果下面列出的所有资源包都被定义,则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"
});