加载JavaScript文件
RequireJS的目的是鼓励代码的模块化,它使用了不同于传统script标签的脚本加载步骤。可以用它来加速、优化代码,但其主要目的还是为了代码的模块化。它鼓励在使用脚本时以module ID替代URL地址。
RequireJs以一个相对于baseUrl的地址来加载所有的代码。页面顶层script标签含有一个特殊属性data-main,require.js使用它来启动脚本加载过程,而baseUrl一般设置到与该属性相一致的目录。
<script data-main="scripts/main.js" src="scripts/requires.js"></script>
baseUrl亦可通过RequireJS config手动设置。如果没有显式指定config及data-main,则默认的baseUrl为包含RequireJS的那个HTML页面的所属目录。
<script data-main="js/app.js" src="js/require.js"></script>
//app.js
require.config({
baseUrl:'js/lib',
paths:{
app:'../app'
}
});
require(['jquery','canvas','app/sub'],function($,canvas,sub){
//....
})
注意在示例中,三方库如Jquery没有将版本号包含在它们的文件名中。我们建议将版本信息放置在单独的文件中进行跟踪。
理想状况下,没个加载的脚本都是通过define()来定义的一个模块;但是有些浏览器全局变量注入型的传统/遗留库并没有使用define()来定义它们的依赖关系,你必须为此使用shim config来指明它们的依赖关系。如果你没有指明依赖关系,加载可能报错。这是因为基于速度的原因,srequireJS会异步地以无序的形式加载这些库。
data-main入口点
require.js在加载的时候会检查data-main属性:
<script data-main="scripts/main" src="scripts/requires.js"></script>
你可以在data-main指向的脚本中设置模板加载选项,然后加载第一个应用模块。注意:你再main.js中所这是的脚本是异步加载的。所以如果你在页面中配置了其它JS加载,则不能保证它们所依赖的JS已经加载成功。
<script data-main="scripts/main" src="scripts/require.js"></script>
<script src="scripts/other.js"></script>
//main.js
require.config({
paths:{
foo:'libs/foo-1.1.3'
}
});
//other.js
require(['foo'],function(foo){})
定义模块
模块不同于传统的脚本文件,它良好的定义了一个作用域来避免全局名称空间污染。它可以显式的列出其依赖关系,并以函数参数的形式将这些依赖进行注入,而无需引用全局变量。requireJS的模块是模块模式的一个扩展,其好处是无需全局引用其它模块。
RequireJs模块语法允许它尽快地加载多个模块,虽然加载的顺序不定,但依赖的顺序最终是正确的。同时因为无需创建全局变量,甚至可以做到在同一页面上同时加载同一模块的不同版本。
如果一个模块仅含值对,没有任何依赖,则在define()中定义这些值就好了:
define({
color:'black',
size:'unisize'
})
如果一个模块没有任何依赖,但需要一个setup工作的函数,则在define()中定义该函数,并将其传给define():
define(function(){
return{
color:'black',
size:'unisize'
}
})
如果模块存在依赖:则第一个参数是依赖的名称数组;第二个参数是函数,在模块的所有依赖加载完毕后,该函数会被调用来定义该模块,因此该模块应该返回一个定义了本模块的object。依赖关系会以参数的形式注入到该函数上,参数列表与依赖名称列表一一对应。
define(['./cart','./inventory'],function(cart,inventory){
return{
color:'blue',
size:'large',
addToCart:function(){
inventory.decrement(this);
cart.add(this);
}
}
})
对模块的返回值类型并没有强制为一定是个object,任何函数的返回值都是允许的。此处是一个返回了函数的模块定义:
define(['my/cat','my/inventory'],{
function(cart,inventory){
return function(title){
return title?(window.title = title):inventory.storeName+''+cart.name;
}
}
})
define()中的相对模块名:为了可以在define()内部使用诸如require('./relative/name')的调用以正确解析相对名称,记得将require本身作为一个依赖注入到模块中:
define(['require','./relative/name'],funciton(require){
var mod = require('./relative/name')
})
或者更好地,使用下述转换为CommonJS模块所设的更短的语法:
define(function(require){
var mod = require('./relative/name')
})
相对路径在一些场景下格外有用,例如:为了以便于将代码共享给其他人或项目,你在某个目录下创建一些模块。你可以访问模块的相邻模块,无需知道该目录的名称。
生成相对于模块的URL地址:你可能需要生成一个相对于模块的URL地址。你可以将require作为一个依赖注入进来,然后调用require.toUrl()以生成URL:
define(['require'],function(require){
var cssUrl = require.toUrl('./style.css')
})
如果你定义了一个循环依赖(a依赖b,b同时依赖a),则在这种情形下当b的模块函数被调用的时候,它会得到一个undefined的a。b可以在模块已经定义好后用require()方法再获取(记得将require作为依赖注入进来):
//b.js
define(['require','a'],function(require,a){
return function(title){
return require('a').doSomething();
}
})
一般说你无需使用require()去获取一个模块,而是应当使用注入到模块函数参数中的依赖。循环依赖比较罕见,它也是一个重构代码重新设计的i警示灯。
如果你熟悉CommonJS,你可以考虑使用exports为模块建立一个空object,该object可以立即被其它模块引用。再循环依赖的两头都如此操作之后,你就可以完全持有其它模块了。这种方法仅在每个模块都是输出object作为模块值的时候有效,换成函数无效。
//b.js
define(function(require,exports,module){
var a = require('a');
exports.foo = function(){
return a.bar();
}
})
或则,如果你使用依赖注入数组的步骤,则可用注入特殊exports来解决:
//b.js
define(['a','exports'],function(a,exports){
exports.foo = function(){
return a.bar();
}
});
JSONP是在javascript中服务调用的一种方式。他仅需简单地通过一个script标签发起HTTP GET请求,是实现跨域服务调用一种公认手段。
为了在RequireJS中使用JSON服务,须要将callback参数的值指定为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);
}
);