RequireJS 2.0 API之配置项
技术性细节§ 2
RequireJS 把每一个依赖项当做一个script标签,使用 head.appendChild()来加载。
RequireJS 会计算好依赖关系,按照正确的顺序依次加载所有依赖项。然后才调用模块的构造函数。
在能同步加载模块的服务端JS中使用 RequireJS 也很容易,只需要重定义require.load()。可以用构建系统来做这个,服务端的 require.load 方法在build/jslib/requirePatch.js 中。
未来,这个代码可能会被当作一个可选的模块放到 require/ 目录中。这样你就可以在基于主机环境来使用正确的加载模式。
配置项§ 3
当我们在顶层页面(或者没有定义一个模块的顶层脚本文件)中使用 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.js加载前定义一个全局的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"的 script 标签 src="/another/path/my/module.js"。加载普通.js文件的时候, baseUrl 将不会起作用,script的src会直接使用 那些字符串,所以 a.js 和 b.js 会从HTML页面的同级目录中加载。
如果没有明确的配置 baseUrl ,它的默认值是会是加载require.js的HTML页面所在目录。如果使用 了data-main ,那么baseUrl会被设置成data-main指定的脚本所在目录。
baseUrl 可以与加载 RequireJS 的页面是不同域。 RequireJS 是可以加载跨域脚本的。 唯一的例外是,使用text! 插件加载的文本模块:这些路径必须与页面是在同一个域下,至少在开发的时候是这样的。当使用了 优化工具 后,跨域的文本模块会被打包进来,这时就加载跨域的文本模块了。
paths: 映射到不能直接在baseUrl下找到的模块名。 通常, path 设置的路径是相对于 baseUrl 的,除非 以 "/" 开头或包含URL协议 (例如" http:")。例如上例中,"some/module" 模块的最终生成的script标签的src="/another/path/some/v1.0/module.js"。 path的设置不要加.js后缀,因为path也可能是映射到一个目录。 如果path映射的是一个模块,RequireJS会自动加上.js后缀。
shim: 为那些没有使用 define() 声明依赖项、没有设置模块值、老的、传统的"浏览器全局"脚本配置依赖项和exports。例如 (RequireJS 2.1.0+):
requirejs.config({
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'
},
'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.
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" 属性一样。 "init" 属性用于RequireJS 2.1.0+ 中,如此, exports配置的字符串值可用于 enforceDefine,也可用于类库加载后的一些功能性工作。
像 jQuery 或者 Backbone 插件这种不需要导出一个模块值的模块,可以用 shim 只配置一个表示依赖项的数组:
requirejs.config({
shim: {
'jquery.colorize': ['jquery'],
'jquery.scroll': ['jquery'],
'backbone.layoutmanager': ['backbone']
}
});
然而如果你想要在IE中检测404并执行fallbacks 或者errbacks,那么就必须配置 exports ,这样加载器才能检测脚本是否加载成功:
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只是配置模块的依赖关系。需要加载 模块的话,还是需要用 调用require/define 。 设置shim不会触发加载代码。
- 确保"shim"配置的模块只当另外一个"shim"配置的模块或者没有依赖关系的AMD模块的依赖模块,并且在在他们创建一个全局变量后调用define()(比如 jQuery ,lodash)。 另外,如果你使用一个 AMD 模块当shim配置的模块的依赖项,打包后, 那个AMD模块在shim配置的模块代码运行前可能不会被赋值 ,并且会抛出一个错误。 最终的解决方案是升级shim配置的模块的代码,加一个可选的 AMD define() 调用。
优化工具配置中的 "shim" 的重要注意事项:
- 你需要在 mainConfigFile打包配置 中指定shim配置项所在的配置文件。 否则,优化工具找不到 shim 配置。 另一种做法是在打包的配置文件中保留shim配置的副本。
- 不要在shim配置中混合使用 CDN 加载。 案例:你从CDN加载jQuery,但从本地加载依赖jQuery的Backbone 。当你打包的时候,确保内置jQuery到打包的文件中并且不要从CDN加载。否则,Backbone也会被内置到打包的文件并且在 从CDN加载的jQuery加载前执行。 这是因为shim配置的模块只是延时到依赖模块加载完后再加载, 但是不会自动的使用define包装。 打包后,Backbone 内置了,而来自CDN的jQuery不会被内置,Backbone 会在jQuery加载前、文件加载后执行,而不能在依赖模块jQuery加载后再执行define包装的代码。 define()包装的模块可以与 CDN加载的代码一起使用,因为这些模块正确的使用了define工厂函数进行了包装,这使得他们在依赖项加载前是不会执行的。总结: shim配置是一个为了解决非模块代码,历史代码的方法。 用define()包装的模块更好。
- 如果你使用uglifyjs来压缩你的代码,不要设置uglifyjs的配置项
toplevel
为 true,或者在用命令行的时候不要使用参数-mt。
这些方法会破坏shim需要使用的全局变量名。 -
map: 对于给定的相同的模块名,加载不同的模块,而不是加载相同的模块。
这种特性在某些大型项目是非常有用的。例如那种有两套不同的模块依赖两个不同版本的'foo'模块,并且这两套模块需要相互协作。
这时 使用context配置来支持多版本模块加载 是不行的。这种情况下, paths config 只能设置模块的根路径,而不是映射一个模块到另一个。
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的值也可以是"*",表示全匹配,即所有模块遵循这一设置。如果还有其他匹配项,将会比"*"的配置优先级高。例如:
requirejs.config({ map: { '*': { 'foo': 'foo1.2' }, 'some/oldmodule': { 'foo': 'foo1.0' } } });
在`require('foo')` 时,除了"some/oldmodule" 中会加载 "foo1.0", 其他模块都加载"foo1.2" 。
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.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/jrburke/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; });
packages: 配置从CommonJS 包来加载模块。详见 packages topic。
waitSeconds: 放弃加载脚本前的等待的秒数。 设置为 0 则禁用此功能。默认是 7 秒。
context: 加载上下文配置(require.config的对象)的名字。 只要顶级 require调用指定一个唯一context字符串,require.js就可以在一个页面中加载多个版本的模块。要正确的使用它,参考 Multiversion Support 。
deps: 需要加载的依赖项的数组。当在require.js加载前使用全局 require对象来定义配置的时候很有用,也可以在require()一定义后就马上加载指定依赖项的时候用。
callback: 所有依赖项加载后执行的回调函数。 当在require.js加载前使用全局 require对象来定义配置的时候很有用,也可以用在require.config中。
enforceDefine: 如果设置为true, 当加载的脚本是没用define()包装过,且在shim配置中没有配置exports值时会抛错。 详见 Catching load failures in IE 。
xhtml: 如果设置为 true,requireJS 将使用document.createElementNS() 来创建script标签。
urlArgs:RequireJS 用来匹配资源的额外的 URL的查询参数 。通常的用法是在浏览器或者服务器配置不对的时候禁用缓存。例如:
urlArgs: "bust=" + (new Date()).getTime()
这在开发的时候很有用,但是在部署的时候最好删除它。
scriptType: 设置 RequireJS生成的 script 标签的 type属性值。默认是"text/javascript"。 可设置为"text/javascript;version=1.8" 来使用Firefox's JavaScript 1.8特性。