RequireJs的理解

前面有一篇博文提到了RequireJs,那么这篇主要是从某个实际项目中的应用来总结一下RequireJs。

RequireJs项目的应用

拿出最近某个同事使用RequireJs写的一个项目,来分析一下其中的代码。

目录结构:

RequireJs

HTML引入:

1
<script src="js/vendor/require.js" data-main="js/main"></script>

main.js的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require.config({
baseUrl: '.',
paths: {
'js': 'js',
'jquery': 'js/vendor/jquery.min',
'swiper': 'js/vendor/swiper2.min',
'waypoints': 'js/vendor/jquery.waypoints.min',
'jquery.inview': 'js/vendor/jquery.inview.min'
},
shim: {
'waypoints': {
deps: ['jquery'],
exports: 'jQuery.fn.waypoint'
},
'jquery.inview': {
deps: ['jquery']
}
}
});
require(['jquery', 'waypoints', 'swiper', 'jquery.inview'], function($, waypoint, Swiper) {
// code here!
});


关于HTML引入RequireJs

使用RequireJs的话,我们只需要导入RequireJs即可,不需要显式导入其它的js库,因为这个工作会交给RequireJs来做。
属性data-main是告诉RequireJs:你下载完以后,马上去载入真正的入口文件。它一般用来对RequireJs进行配置,并且载入真正的程序模块。

关于main.js

main.js 中通常用来做两件事:

  1. 使用require.config()配置RequireJs。
    baseUrl:与paths是相关的,指定paths里文件的基准目录。
    paths:指定项目中用到哪些模块,文件路径是什么等等。
    shim:将某个依赖中的某个全局变量暴露给requirejs,当作这个模块本身的引用。

  2. 使用require()函数载入“主模块”。

关于模块

我们可以把main.js可以理解为所有模块的入口(称为“主模块”)。通常在项目中,都在main.js中使用require()函数来引入依赖的其他模块。在后文中,我把每一个引入到main.js的js文件都称作为模块。

所有模块都必须按照AMD的规定来写,即采用特定的define()函数来定义。有两种情况:

  • 模块不依赖其他模块:
    直接用define()函数,传入一个函数即可。
  • 模块还依赖其他模块:
    使用define()函数传入两个参数,第一个参数必须是一个数组,指明该模块依赖的模块;第二个参数是函数。当require()加载这个模块的时候,就会先加载当前这个模块依赖的模块。

当然,如果一个模块没有按照AMD的规定来写,也可以被依赖。就需要在require.config()的shim参数中定义。

例如有一个未按照AMD的规定来写的模块hello.js:

1
2
3
function hello() {
alert("hello, world~");
}

先看下面不能正确工作的代码:

1
2
3
4
5
6
7
8
9
10
requirejs.config({
baseUrl: '/public/js',
paths: {
hello: 'hello'
}
});
requirejs(['hello'], function(hello) {
hello();
});

这段代码会报错,提示:

1
Uncaught TypeError: undefined is not a function

原因是最后调用 hello() 的时候,这个 hello 是个undefined . 这说明,虽然我们依赖了一个js库(它会被载入),但requirejs无法从中拿到代表它的对象注入进来供我们使用。
在这种情况下,我们要使用 shim ,将某个依赖中的某个全局变量暴露给requirejs,当作这个模块本身的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
requirejs.config({
baseUrl: '/public/js',
paths: {
hello: 'hello'
},
shim: {
hello: { exports: 'hello' }
}
});
requirejs(['hello'], function(hello) {
hello();
});

再运行就正常了。

上面代码exports:'hello'中的hello,是我们在hello.js中定义的hello函数。当我们使用function hello() {}的方式定义一个函数的时候,它就是全局可用的。如果我们选择了把它export给requirejs,那当我们的代码依赖于hello模块的时候,就可以拿到这个hello函数的引用了。

补充

关于paths参数中配置的'js': 'js',目的是为了省略后面的扩展名.js。



参考资料:
haorooms博客:http://www.haorooms.com/post/requirejs_sy_lj
AMD规范:https://github.com/amdjs/amdjs-api/wiki/AMD

—— end —— 注:水平有限,难免有误,如有错误欢迎指出!