一、使用RequireJs的好处
最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。
这些js文件都是按照顺序从上到下依次同步加载的。
这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的a.js要在b.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
RequireJs很好的解决了两个问题及其优势:
1、实现js文件的异步加载,避免网页失去响应;
2、管理模块之间的依赖性,便于代码的编写和维护。
3、基于AMD模块化机制,让前端代码也能实现模块化。查看《CommonJS和AMD/CMD区别详解》。
开始今天的学习之前,首先需要准备4个文件,分别是【jQuery库】、【require库】、【bootstrap库】、【require加载css插件】,大家可以点击下面的链接进行下载:
1、jquery.js
2、require.js
3、bootstrap.min.js
4、require.css.js
二、如何使用RequireJs?
1、在html页面中引入require库,如下:
2、加载require.js以后,下一步就要加载我们自己的代码了。假定我们自己的代码文件是main.js,也放在js目录下面。那么,只需要写成下面这样就行了:
data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main(data-main引入的main.js文件是异步加载的,所以在加载这个文件的同时也会加载其他js文件,其他文件要依赖这个main.js,所以可以将以上代码分开来写)。
3、主模块main.js的写法:
主模块main.js主要是加载requireJs的配置项,所以需要先了解一下这些配置项的意思:
(1)baseUrl:模块默认加载路径;
(2)paths:自定义模块加载路径;
(3)shim:定义模块之间的依赖关系。
下面具体来看下,这是我的目录结构:
4 |
'jquery' : '../js/jquery' , |
|
html页面调用:
1 |
require([ 'jquery' ], function (a){ |
2 |
console.log(a);// function n() |
|
require方法的第一个参数是数组,指明需要加载的模块,第二个参数是一个匿名回调函数,里边是具体要执行的代码。
回调函数中的参数a是jquery.js模块返回的对象(名字可以自定义,也可以是b,c,d等等),其实就是一个$,这个可以从打印结果中看出来。
正常调用jquery中的方法:
1 |
require([ 'jquery' ], function (a){ |
2 |
$( 'body' ).css({ 'background-color' : '#f00' }); |
|
下面来看看require如何自定义各个模块之间的依赖关系:
05 |
'jquery' : '../js/jquery' , |
06 |
'bootstrap' : '../js/bootstrap.min' , |
14 |
'deps' : [ 'jquery' , 'bootstrap' ] |
|
上面的shim中是json数据,例如bootstrap依赖于jquery,test依赖于jqueyr和bootstrap,所以它们的加载顺序是jqueyr、bootstrap、test,浏览器中的加载顺序截图如下:
调用如下:
1 |
require([ 'bootstrap' , 'test' ], function (){ |
|
4、用define自定义模块:
例如我在app文件夹新建一个util.js文件,代码如下:
01 |
define([ 'jquery' ], function (){ |
03 |
'changeBgColor' : function (){ |
04 |
$( 'body' ).css({ 'background-color' : '#f00' }); |
06 |
'changeTxtSize' : function (){ |
07 |
$( 'body' ).css({ 'font-size' : '30px' }); |
|
同样的,define方法的第一个参数也是一个数组,是return的json数据中需要用到的模块,这些模块也是从main.js中进行加载,所以在使用define自定义模块之前需要先配置好main模块。
调用代码如下:
1 |
require([ 'util' ], function (a){ |
|
因为define方法的第一个参数中引用的模块,是return出的json中包含的方法都共用的,如果有些方法不需要jquery这个模块,就可以单独进行调用,jquery模块也不需要写在公共的数组当中,代码如下:
01 |
define([], function (){ |
03 |
'changeBgColor' : function (){ |
04 |
$( 'body' ).css({ 'background-color' : '#f00' }); |
06 |
'changeTxtSize' : function (){ |
07 |
require([ 'jquery' ], function (){ |
08 |
$( 'body' ).css({ 'font-size' : '30px' }); |
|
可以实现同样的效果!
刚才使用define方法是requireJs标准化的AMD写法,但是有些时候使用的是非标准化的写法,比如还是刚刚的util文件,里边的代码如下:
1 |
function changeColor(){ |
2 |
$( 'body' ).css({ 'background-color' : '#f00' }); |
|
main.js中需要添加export代码:
05 |
'jquery' : '../js/jquery' , |
06 |
'bootstrap' : '../js/bootstrap.min' , |
14 |
'deps' : [ 'jquery' , 'bootstrap' ] |
17 |
'export' : 'changeColor' |
|
html中的调用代码如下:
1 |
require([ 'util' , 'jquery' ], function (){ |
|
以上效果是正常的,但同时又出现一个问题,如果util中不止有一个方法,那么uitl中的export应该指向哪个方法呢?所以可以用下面的这种方法代码:
05 |
'jquery' : '../js/jquery' , |
06 |
'bootstrap' : '../js/bootstrap.min' , |
14 |
'deps' : [ 'jquery' , 'bootstrap' ] |
19 |
'changeColor' : changeColor, |
20 |
'changeTxtSize' : changeTxtSize |
|
调用如下:
1 |
require([ 'util' , 'jquery' ], function (a){ |
|
原理就是将单个的函数方法添加到init中组成json对象,然后把这个对象传递给参数a,由a统一进行调用。
5、requireJs插件的使用:
本来requireJs只能嗲用js模块,其他例如css层级样式表是无法调用的,但是通过插件可以实现requireJs调用css样式,代码如下:
05 |
'jquery' : '../js/jquery' , |
07 |
'bootstrap' : '../js/bootstrap.min' |
11 |
'deps' : [ 'jquery' , 'css!../css/style.css' ] |
|
主要是通过调用bootstrap.js,然后有了bootstrap.js对jquery和style.css的依赖关系,调用style.css。
html进行调用:
1 |
require([ 'bootstrap' ], function (a){ |
|
最后总结下RequireJs的原理:
(1)我们在使用requireJS时,都会把所有的js交给requireJS来管理,也就是我们的页面上只引入一个require.js,把data-main指向我们的main.js。
(2)通过我们在main.js里面定义的require方法或者define方法,requireJS会把这些依赖和回调方法都用一个数据结构保存起来。
(3)当页面加载时,requireJS会根据这些依赖预先把需要的js通过document.createElement的方法引入到dom中,这样,被引入dom中的script便会运行。
(4)由于我们依赖的js也是要按照requireJS的规范来写的,所以他们也会有define或者require方法,同样类似第二步这样循环向上查找依赖,同样会把他们村起来。
(5)当我们的js里需要用到依赖所返回的结果时(通常是一个key value类型的object),requireJS便会把之前那个保存回调方法的数据结构里面的方法拿出来并且运行,然后把结果给需要依赖的方法。
(完)!