综述
RequireJS 遵循 AMD 规范(异步模块定义)。理论上来说,RequireJS 加载的模块必须符合 AMD 规范,使用 define() 函数定义。但是,由于历史原因,大量的 js 库并不符合规范。这样加载非规范的模块,就需要用到 shim
比如 underscore 和 backbone 都不符合 AMD 规范,如果要加载他们,就要像这样使用:
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
exports
是模块的返回值,该值一定要与库暴露的全局变量名称一致。
deps
表明模块的依赖,如果 A 模块依赖于 B 模块,而 A 模块不符合 AMD 规范,使用全局变量,那么 B 模块也必须使用全局变量,不然 A 模块会找不到 B 模块的依赖对象而报错。
准备
下面开始做实验。
File: test.html
<!DOCTYPE html>
<html>
<head>
<title>shim</title>
<script src="https://cdn.bootcss.com/require.js/2.3.5/require.min.js" data-main="main.js"></script>
</head>
<body>
</body>
</html>
File: a.js
// 立即执行函数,不暴露私有成员
(function(window){
var a = {};
a.sayHello = function(){
alert("Hello this is A");
return 'Hello';
}
window.a = a;
})(window)
File: main.js
require(['a'],function(a) {
console.log(a);
});
载入文档,在 console 中我们会看到,输出 undefined
但是我们查看浏览器的 element 和 network 会发现,实际上 a.js 已经被加载进来了。
下面我们在 console 中输入:
a.sayHello();
可以看出实际上 a.js 中暴露的全局变量还是存在的。
使用 shim
下面我们用 shim
让 RequireJS 可以获得暴露的全局变量作为返回值。
修改 main.js
requirejs.config({
shim: {
a:{
deps:[],
exports: 'a'
}
}
});
require(['a'],function(a) {
console.log(a);
});
然后刷新文档,这次肯定是输出内容了。
如果我们把 exports: 'a'
改成 exports: 'something'
那么再刷新,一定会发现又输出 undefined
所以,exports
是模块的返回值,该值一定要与库暴露的全局变量名称一致。
使用 deps
下面来实验 deps
的用法,假设 a.js 依赖于 b.js
正确的写法
File: b.js
var b = {};
console.log('b');
b.string = 'Hello, this is a string from b.js';
b.getString = function() {
return this.string;
}
b.setString = function(string) {
this.string = string;
}
File: a.js
// 立即执行函数,不暴露私有成员
(function(window){
console.log('a');
var a = {};
a.sayHello = function(){
alert("Hello this is A");
return 'Hello';
}
a.getBString = function() {
alert(b.getString());
}
window.a = a;
})(window)
File:main.js
requirejs.config({
shim: {
a:{
deps:['b'],
exports: 'a'
},
b: {
deps:[],
exports: 'b'
}
}
});
require(['a'],function(aAlias) {
aAlias.getBString();
});
效果如下:
这是标准的写法,下面我们来实验一个不好的写法。
错误的写法
将 main.js 改成
requirejs.config({
shim: {
a:{
deps:[],
exports: 'a'
},
b: {
deps:[],
exports: 'b'
}
}
});
require(['a','b'],function(aAlias, bAlias) {
aAlias.getBString();
});
我们并没有声明 a.js 依赖 b.js 而是在使用的时候将 b.js require 进来了。这样在本例中也是正常的。但仅仅是因为本例太简单,对执行的先后顺序没有要求而已,换个场景可能就失效了。
require(['a','b'],function(aAlias, bAlias)
仅仅表示 a b 都加载完成后,执行回调函数,但是 加载的顺序是不固定的。执行的顺序是固定的,按照依赖声明的先后顺序执行。
在上面我们埋了 console.log('a)
和 console.log('b')
那么在不好的写法中,控制台中打印的顺序是不固定的,有时候是 a b
有时候是 b a
,而正确的例子中,指定顺序是固定的,一定是 b a
下面是错误的写法,多次刷新的结果,可以看出,顺序是有变化的。
参考文档
使用requireJS加载不符合AMD规范的js文件:shim的使用方式和实现原理
Javascript模块化编程(二):AMD规范