§ 1. 用法
§ 1.1. 加载js脚本文件
为了鼓励模块化开发,使用模块ID来进行模块代码加载。
RequireJS通过baseUrl的相关路径来进行代码加载。
baseUrl一般设置为 data-main属性对应文件的父目录。
(RequireJS会检查并运行data-main所指定的脚本)
html内设置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>
baseUrl也可以通过config来进行手动设置。
如果没有使用script标签,或config进行设置,baseUrl将会是运行RequireJS的html文件目录。
RequireJS默认以"baseUrl和paths"方式加载脚本,如果不想使用baseUrl和paths的方式加载脚本,可以是使用下面的方法:
1.带".js"结尾
2.以"/"开始
3.含"http:"或"https:"的url地址
如果想要清晰的分割目录结果,可以使用下面的方法,但不推荐
www/
- index.html
- js/
- app/
- sub.js
- lib/
- jquery.js
- canvas.js
- app.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. });
RequireJS也可以引用用户自己通过define()方法定义的模块,且可以定义该模块的依赖模块
另外,对于浏览器级别的脚本的,不能通过define()来定义依赖模块,需要通过shim config来定义他们的依赖模块
§ 1.2. data-main 入口
代码如下:
<!--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 可以同时设置config与入口模块
RequireJS无法保证 data-main 模块会优先于 其他通过<script>标签引用的脚本 被加载完成并执行。
例如下面的代码有时会执行失败:
<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()方法,只用引用RequireJS,不要加data-main属性即可,例如:
<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模块)
每个模块必须定义在一个文件里,这些模块可以通过优化工具,被打包压缩。
§ 1.3.1. 简单键/值对[json格式类似] 没有依赖模块,没有方法,只有数据
//Inside file my/shirt.js: define({ color: "black", size: "unisize" });
§ 1.3.2. 定义方法
没有依赖模块,但需要处理些事情准备时的定义模块方法:
//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); } } } );
§ 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; } } );
§ 1.3.5. 简单CommonJS模块的兼容定义
需要直接引用模块名,代码如下:
define(function(require, exports, module) { var a = require('a'), b = require('b'); //Return the module value return function () {}; } );
该格式需要Function.prototype.toString()方法获取方法内容,不能在PS3和一些旧Opera浏览器中使用,但在优化工具优化压缩后正常运行。
§ 1.3.6. 定义带模块名的模块
模块名作为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. 其他的模块方面的提示:
1.一个js文件,一个模块。 可以使用优化工具,将开发完的所有代码优化后,会进行压缩,整合
2.可以在模块内部使用require方法
define(["require", "./relative/name"], function(require) { var mod = require("./relative/name"); });
define(function(require) {
var mod = require("./relative/name"); });
3.生成相对于模块的url
define(["require"], function(require) { var cssUrl = require.toUrl("./style.css"); });
4.控制台内的调试
对于已经加载的模块,可以通过下面方法获取他
require(["module/name"], function(){})
require("module/name").callSomeFunction()
相对路径只在内部定义可用。
§ 1.3.8. 循环依赖
"a" needs "b" and "b" needs "a"
b内的a为null
应该用下面方法:
//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(); } } );
也可以使用CommonJS模块的方法,但是必须每个模块返回的都是对象,而不是方法
//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(); }; });
或
//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(); }; });
§ 1.3.9. 特别的JSONP服务的依赖
JSONP:js通过http Get方法的跨域调用WebService的方法
通过回调参数获取JSONP URL返回的数据
代码如下:
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超时做出充分的错误处理
只有JSONP返回JSON数据时,才能使用该方法。返回数组,字符串或数字时,不能使用该方法。且不适用于长轮询JSONP,如果查复通过同一个JSONP URL获取,只能获取到缓存数据
你可以重写requirejs.onError()方法去捕获错误。
§ 1.3.10. 解除模块定义
RequireJS里有一个全局函数requirejs.undef()可以用来解除模块定义,但是无法移除已经依赖完成的模块内的定义,一般用于错误时的处理
§ 2. 原理
RequireJS的加载依赖JavaScript模块是通过 head.appendChild() 来实现的
RequireJS会按照正确的顺序加载脚本模块
如果用于服务器端时,可以修改require.load()方法,使得RequireJS同步加载脚本模块
require.load位于 build/jslib/requirePatch.js
§ 3. 配置选项
§ 3.1. 可以直接在html内配置
<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入口。
当然也可以在载入requireJS前,定义全局配置对象 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>
§ 3.2. 支持的配置选项
baseUrl: 查找所有模块的根目录。通过插件,可以做到跨域加载。
paths: 罗列了不从baseUrl里查找的模块。一般paths是相对于baseUrl的路径,除非是通过'/'或'URL形式'定义的路径
bundles: 打包定义模块,代码如下:
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' });
skim: 加载不能用define()定义的模块,如:全局定义,exports等非AMD规则库
例如下面:
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({}); });
关于使用 jQuery或Backbone插件,可以通过下面方式定义:
requirejs.config({ shim: { 'jquery.colorize': ['jquery'], 'jquery.scroll': ['jquery'], 'backbone.layoutmanager': ['backbone'] } });
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' } } });
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
When 'some/newmodule' does `require('foo')` it will get the foo1.2.js file, and when 'some/oldmodule' does `require('foo')` it will get the foo1.0.js file.
只有在AMD规范下才起作用,且必须使用绝对路径。
也支持*号
requirejs.config({ map: { '*': { 'foo': 'foo1.2' }, 'some/oldmodule': { 'foo': 'foo1.0' } } });
Means that for any module except "some/oldmodule", when "foo" is wanted, use "foo1.2" instead. For "some/oldmodule" only, use "foo1.0" when it asks for "foo".
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; });
也可为包配置:
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: 使得带js后缀与不带的模块ID相同
waitSeconds: 加载超时时间。0:无超时,默认7秒
context: 多版本支持
deps: 依赖
callback: 依赖模块加载完后的回调
enforceDefine: 若模块没有define()或shim exports string value会报错
xhtml: true时,会用document.createElementNS() 创建脚本元素
urlArgs: URL参数
urlArgs: "bust=" + (new Date()).getTime()
在开发阶段有用
scriptType: 脚本类型版本,默认:text/javascript
skipDataMain: 跳过入口属性扫描直接加载模块,用于使用多个 由RequireJS开发的模块