require.js读书笔记 2.usage
REQUIREJS API
This is the RequireJS 2.0 API. If you want 1.0: Link to 1.0.- Usage§§ 1-1.3
- Load JavaScript Files § 1.1
- data-main Entry Point§ 1.2
- Define a Module§ 1.3
- Simple Name/Value Pairs§ 1.3.1
- Definition Functions§ 1.3.2
- Definition Functions with Dependencies§ 1.3.3
- Define a Module as a Function§ 1.3.4
- Define a Module with Simplified CommonJS Wrapper§ 1.3.5
- Define a Module with a name§ 1.3.6
- Other Module Notes§ 1.3.7
- Circular Dependencies§ 1.3.8
- Specify a JSONP Service Dependency§ 1.3.9
- Undefining a Module§ 1.3.10
- Mechanics§§ 2
- Configuration Options§§ 3
- Advanced Usage§§ 4-4.6
- Loading Modules from Packages§ 4.1
- Multiversion Support§ 4.2
- Loading Code After Page Load§ 4.3
- Web Worker Support§ 4.4
- Rhino Support§ 4.5
- Handling Errors§ 4.6
- Loader Plugins§§ 5-5.4
USAGE§ 1
Load JavaScript Files 加载javascript文件夹§ 1.1
requireJs用一种新的script加载方法,这种方法和传统<script>标签是完全不同的。它可以运行地更快,并且进行更好地优化,它的主要目的就是为了支持(encourage)模块化(modular)代码的加载。作为其中的一部分,它支持利用模块ID来加载script,而不是script标签里的url属性。
requireJs加载的所有代码地址都是相对于baseUrl的。页面顶层script标签有一个特殊的属性data-main,require.js用它来启动脚本加载页面,而baseUrl通常设置成这个标签所在的文件夹里。data-main attribute是一个特殊的属性,require.js会用这个属性进行加载。下面这个例子会展示了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可以通过RequireJS config手动(manually)地设置。如果没有明确的(explicit)config设置,或者没有使用data-main属性,那么默认的baseUrl就是包含requireJs的HTML页面所在的目录。
requireJs默认所有依赖(dependence)资源都死scripts,所以写模块ID时不需要.js的后缀。requireJs翻译模块ID的路径时会自动加上.js尾缀的。运用paths config标签,你可以设置一组scripts脚本的位置。相对于<script>标签,这些能让你用更少的字符来加载script。
有时候你想直接饮用一个script,而不是依照(conform)“baseUrl+paths"规则来找它。如果一个模块ID由以下之一的规则,这个ID就不会通过”baseUrl+paths"配置来加载script,而是像普通的script url属性来加载。
- 以.js结束
- 以“/”开始
- url协议(protocol),像 "http:" or "https:".
通常地说,最好通过baseUrl和paths来设置模块ID的路径。这样所,你可以很方便地重命名和重定位脚本(configuring the paths to different locations)。
相似的,为了避免配置凌乱,最好避免多级嵌套(deep folder hierarchies)的方式来加载代码。要么将所有的scripts放在baseUrl的目录中,不然将你的代码分置为目录(library)/第三方目录库(vendor)的结构,可以像以下所示:
- www/
- index.html
- js/
- app/
- sub.js
- lib/
- jquery.js
- canvas.js
- app.js
- app/
in index.html:
<script data-main="js/app.js" src="js/require.js"></script>
and in app.js:
requirejs.config({
//设置默认模块ID的路径 js/lib
baseUrl: 'js/lib',
//另外,如果模块ID以app开始,
//它会从js/app目录加载。paths设置时相对于baseUrl的,绝不会包括“.js”的,但是paths设置可以是相对directory的
paths: {
app: '../app'
}
});
开始main app的逻辑。
requirejs(['jquery', 'canvas', 'app/sub'],
function ($, canvas, sub) {
//jQuery, canvas and the app/sub module are all
//loaded and can be used here now.
});
在这个例子中,第三方库(vendor其实是供应商的意思)如jQuery,并没有将它的版本号显示在文件名中。如果你想跟踪版本号,建议新开一个单独的文件来记录,或者你可以用一些工具,像volo,可以将package.json打上版本信息,但文件名还是jQuery.js。这有助于你的配置最小化,避免为每个版本的库设置paths路径。例如,将"jquery"配置成(configure)“jquery-1,7,2"
理想状态下(ideally),每个加载的脚本都是通过define()来定义的一个模块。然而,有些"浏览器全局变量注入"型传统/遗留(legacy)浏览器可能不能用define()来定义它们的依赖关系。为此(for those),你可以用shim config来解析它们的依赖关系。
如果你不想解析它们的依赖关系,你可能会得到一些加载错误,基于速度的原因(for speed),requireJs会异步( asynchronously)、无序(out of order)地加载脚本。
data-main Entry Point§ 1.2
data-main属性是一个特殊属性,require.js在加载脚本的时候会检查(check)它:
<!--当require.js加载的时候,它会忽视script/main.js的其他script标签属性-->
<script data-main="scripts/main" src="scripts/require.js"></script>
你可以在data-main中设置配置选项,然后加载你的第一个应用模块(application module)。注意:require.js的标签加载的模块是异步的async attribute。这意味着,如果你在这个页面加载了其他scripts,则不能保证通过require.js加载的页面可以先于这些脚本加载完毕。
举个例子,以下的构造不能保证foo模块的require.config的路径设置会先于require()foo模块执行:
<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: //这段代码可能会在main.js 的require.config()之前执行。如果这放生了,require.js会加载'scripts/foo.js‘额不是'scripts/libs/foo-1.1.3.js
require(['foo'], function(foo) { });
如果你想在HTML页面中调用require(),最好不要用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>
Define a Module§ 1.3
一个模块不同于传统脚本文件的地方是,它定义了一个模块范围来避免污染全局环境。它明确地列举了依赖的文件,并以函数(定义那个模块的函数)参数的形式将这些依赖注入。RequireJs的模块是Module Pattern的一个扩展,这样的好处是不需要全局地引入其他模块。
RequireJs的模块属性让它们可以尽快地被加载,就算加载是无序的,依赖也会按照争取的顺序。因为没有创建全局变量,所以在一个页面中可以创建多个版本的模块load multiple versions of a module in a page.
(如果你熟悉或者使用过commonjs,那么请看 CommonJS Notes来了解requireJs和CommonJs的映射(map to)关系)
一个磁盘文件应该只定义一个模块。optimization tool工具可以将模块分组优化(grouped into optimized bundles).
Simple Name/Value Pairs§ 1.3.1
如果一个模块没有任何依赖,仅含任何值/对(name/value),则在define()中定义这些值/对就好了。
//Inside file my/shirt.js:
define({
color: "black",
size: "unisize"
});
Definition Functions§ 1.3.2
如果以个模块没有依赖,但是需要一些函数来做一些setup的工作,那就在define中定义该函数:
//my/shirt.js now does setup work
//before returning its module definition.
define(function () {
//Do setup work here
return {
color: "black",
size: "unisize"
}
});
Definition Functions with Dependencies§ 1.3.3
如果一个模块有依赖,第一个参数(arguments)应该是一串依赖名的数组,第二个参数应该是定义的函数。一旦所有的依赖加载完毕,这个函数就会被调用来定义该模块。定义这个模块的函数应该返回一个对象。依赖会以一个参数的形式传给函数,参数列表和依赖名称列表一一对应
//my/shirt.js 有一些依赖, cart和inventory,都和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/cart.js
- my/inventory.js
- my/shirt.js
这个函数有两个参数“cart”和“inventory”。对应的模块以模块名"./cart" 和"./inventory"展示。
这个函数会在my/cart ,my/inventory被加载后调用,并且获得cart和inventory的函数参数。
严重不允许模块定义全局的变量,这样,多版本的模块才能存在于同一个页面。另外,函数的参数顺序应该和依赖模块的顺序一样。
返回的对象定义了"my/shirt"模块。这样定义模块,"my/shirt"就不会以全局对象的方式存在。
Define a Module as a Function§ 1.3.4
模块的并不一定要有返回值。任何函数的返回值(return value)都是允许的。此处有一个模块如它所定义地返回了一个函数:
/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; } } );
Define a Module with Simplified CommonJS Wrapper§ 1.3.5
如果你想重新用一些以CommonJS module format的方式写的代码,而这些代码难以用上述的依赖数组的方式来写,你可以考虑直接将这些依赖对应到本地变量。你可以使用一个简单的commonJs包装来实现simplified CommonJS wrapper:
define(function(require, exports, module) {
var a = require('a'),
b = require('b');
//Return the module value
return function () {};
}
);
这个包装依赖于Function.prototype.toString()将函数内容赋予有意义的字符串。这PS3或者一些老的Opera手机浏览器上表现不好。用optimizer将这些依赖变成一个数组的格式,以便与在这些设备上用。
更多的信息参考 CommonJS page, "Sugar" section in the Why AMD page.
Define a Module with a Name§ 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.
}
);
这些通常是optimization tool生成的。你可以自己命名模块名,但这会使模块更难移植——如果你将文件夹移到另一个目录中,你还得改它们的名字。最好避免对模块硬编码,而让optimization tool来生成模块名。这个工具需要生成模块名,以便将这些模块打成一个包(be bundled),使浏览器更快加载。
Other Module Notes§ 1.3.7
One module per file.: 每个js文件只能定义一个模块,这是模块寻找机制的自然要求。多个模块会被 optimization tool打包成一个模块,但你需要用将多个模块放到一个文件夹里.
Relative module names inside define(): 对于define()中("./relative/name")的模块调用,确保将require本身作为一个依赖注入到模块中, 这样,相对路径的名称才能被正确地解析:
define(["require", "./relative/name"], function(require) {
var mod = require("./relative/name");
});
更好的方式是,像commomjs一样使用一个更短的解析:
define(function(require) {
var mod = require("./relative/name");
});
这个模块利用Function.prototype.toString()去寻找require()的调用,并把它们加到依赖的数组中。在require()的工作路径下,代码会被正确的解析。
如果你在一个目录中创建了很多模块,相对路径通常是很有用的。你分享这个目录给其他人,你也可以对这个目录中的其他模块做一些改动,而不必知道模块的名称。
Generate URLs relative to module: 你可能需要生成一个相对于模块的URL. 你可以将require作为一个依赖注入,然后用require.toUrl()来生成一个url:
define(["require"], function(require) {
var cssUrl = require.toUrl("./style.css");
});
Console debugging: 如果你想调用一个已经通过 require(["module/name"] , function(){}) 方式加载的模块,你可以用字符串作为模块名的参数来调用它:
require("module/name").callSomeFunction()
注意,这个只对于已经用"module/name"方式异步加载的模块有效。如果用一个相对路径,像'./module/name',那只会在define内部工作有效。
Circular Dependencies§ 1.3.8
如果你定义了一个循环依赖("a" needs "b" and "b" needs "a"), 那么当"b"模块被调用的时候, 它会得到一个未定义的a值。“b”可以在已经被require()定义好后来获取“a”(确保将require作为一个依赖注入):
//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()来获取模块, 只要将依赖的模块作为参数传给函数就可以了.循环依赖是很少出现的,如果出现了,你就想重新考虑设计问题了。然而,有时候也是需要循环依赖的,如果这样的话,可以考虑上述的代码组织。
如果你非常熟悉commonJs,你也可以用exports的来制造一个空模块,这也可以以依赖的形式被其他模块获取。这样做的话,你可以在循环依赖的任意一方安全地使用其他模块。.这只在每个模块都以object作为模块值输出的时候有效,函数形式无效:
//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模块 'exports' dependency:
//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();
};
});
Specify a JSONP Service Dependency§ 1.3.9
JSONP 是javascript中服务调用的一种方式。它通过script标签发起HTTP GET请求,是实现跨域的一种公认手段。
要在requireJs使用JSONP,要在回调中将参数属性设置为”define“。这意味着你可以将获取到的JSONP URL的值作为一个模块定义。
这里有一个调用JSONP API端点的例子。在这个例子中,JSONP callback参数的值设置为”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服务的初始化initial application setup. 一旦JSONP服务超时,意味着其他通过define()的模块不会执行,所以这种错误处理是不强健的。
Only JSONP return values that are JSON objects are supported.一个JSONP的返回是一个数组,如果是一个字符串或者数字式不会工作的。
这个机制不能被用在long-polling 类的JSONP连接-- 那些用来处理实时流的APIs. 这种类型的api在收到每个返回的时候一般会做script清理,RequireJS只会获取一次JSONP URL—— 后继使用require()或者define()发起的URL会得到一个缓存值。
加载JSONP服务的错误通常以服务超时的形式出现。因为简单加载一个script标签不会得到很多网络错误信息。你可以用override requirejs.onError() 来获取错误。更多信息参见 Handling Errors section.
Undefining a Module§ 1.3.10
有一个全局的函数requirejs.undef(), 用来undefining一个模块.它会重设加载器的内部(internal)状态来消除前一个模块的定义。
然而,如果一个模块已经被定义了,并且在其他模块中作为一个依赖被调用执行了,全局函数是不会清除这个模块的。 所以它仅在无其他模块持有该模块错误的时候有用,或者当未来需要加载这模块时有点用。 See the errback section for an example.
如果想知道更多关于undefined的复杂语法,the semi-private onResourceLoad API may be helpful.

浙公网安备 33010602011771号