requirejs加载顺序_RequireJS - 入门指南、进阶使用详解(附样例)

现在项目大都使用模块化开发,而 RequireJS 作为 AMD模块开发的典范,还是很值得学习使用的。

一、AMD 规范

1,AMD 基本介绍

AMD全称为Asynchromous Module Definition(异步模块定义)

AMD是 RequireJS 在推广过程中对模块定义的规范化产出,它是一个在浏览器端模块化开发的规范。

AMD模式可以用于浏览器环境并且允许非同步加载模块,同时又能保证正确的顺序,也可以按需动态加载模块。

2,AMD 模块规范

AMD通过异步加载模块。模块加载不影响后面语句的运行。所有依赖某些模块的语句均放置在回调函数中。

AMD规范只定义了一个函数 define,通过 define方法定义模块。该函数的描述如下:

define(id?, dependencies?, factory)

id:指定义中模块的名字(可选)。如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字。如果提供了该参数,模块名必须是“顶级”的和绝对的(不允许相对名字)。

dependencies:当前模块依赖的,已被模块定义的模块标识的数组字面量(可选)。

factory:一个需要进行实例化的函数或者一个对象。

AMD规范允许输出模块兼容 CommonJS规范,这时 define方法如下:

define(function (require, exports, module) {

var reqModule = require("./someModule");

requModule.test();

exports.asplode = function () {

//someing

}

});

二、RequireJS 介绍

1,什么是 RequireJS

RequireJS是一个 JavaScript模块加载器。它非常适合在浏览器中使用,但它也可以用在其他脚本环境, 比如 Rhino 和 Node。使用 RequireJS加载模块化脚本将提高代码的加载速度和质量。

2,使用 RequireJS 的好处

异步“加载”。使用 RequireJS,会在相关的 js加载后执行回调函数,这个过程是异步的,所以它不会阻塞页面。

按需加载。通过 RequireJS,你可以在需要加载 js逻辑的时候再加载对应的 js 模块,不需要的模块就不加载,这样避免了在初始化网页的时候发生大量的请求和数据传输。

更加方便的模块依赖管理。通过 RequireJS的机制,你能确保在所有的依赖模块都加载以后再执行相关的文件,所以可以起到依赖管理的作用。

更加高效的版本管理。比如原来我们使用的 script脚本引入的方式来引入一个 jQuery2.x 的文件,但如果有 100个页面都是这么引用的,如果想换成 jQuery3.x,那你就不得不去改这 100个页面。而使用 requireJS只需要改一处地方,即修改 config中 jQuery的 path映射即可。

当然还有一些诸如 cdn加载不到 js文件,可以请求本地文件等其它的优点,这里就不一一列举了。

三、RequireJS 的配置和使用

1,下载最新版的 require.js

2,创建一个如下目录结构

(1)lib文件夹下放置一些需要用到的 js库,这里除了 require.js 外,还有 jquery。

(2)script文件夹下放置 RequireJS的入口 js、以及模块 js文件。

(3)index.html则为主页面。

1f95a1a7382d0d52a152ae125150eab9.png

3,效果图

(1)页面初始化的时候显示一个按钮。

(2)点击按钮会调用 hello模块的方法,将信息显示在页面上。

f1011d657313c487986bf3dbd5819538.png      

c0f66a973b50b8b37237e576ecb3b1bc.png

4,代码说明

(1)index.html

hangge.com

点击

注意 script标签,除了指定 require.js路径外,还有个 data-main属性:

这属性指定在加载完 reuqire.js后,就用 requireJS加载该属性值指定路径下的 JS文件并运行,所以一般该 JS文件称为主 JS文件(其 .js 后缀可以省略)。

(2)main.js

require.config({

baseUrl: 'js',

paths: {

jquery: 'lib/jquery-1.11.1',

}

});

require(['jquery', 'script/hello'],function ($, hello) {

$("#btn").click(function(){

hello.showMessage("hangge.com");

});

});

要改变 RequireJS的默认配置,可以使用 require.configh函数传入一个可选参数对象。下面是一些可以使用的配置:

baseUrl:用于加载模块的根路径。在配置这个属性后,以后的文件都是在这个路径下查找内容了。

paths:用于一些常用库或者文件夹路径映射,方便后面使用,省得每次都要输入一长串路径。(js文件的后缀可以省略)

shim:加载非 AMD规范的 js,并解决其载入顺序。

require方法:

require函数用于加载模块依赖,这里我们加载了 jQuery以及 hello这个自定义模块。在加载完毕的回调中,给按钮添加个点击事件,同时点击后会调用 hello模块中的 showMessage 方法。

(3)hello.js

define(['jquery'],function($){

//变量定义区

var moduleName = "hello module";

var moduleVersion = "1.0";

//函数定义区

var showMessage = function(name){

if(undefined === name){

return;

}else{

$('#messageBox').html('欢迎访问 ' + name);

}

};

//暴露(返回)本模块API

return {

"moduleName":moduleName,

"version": moduleVersion,

"showMessage": showMessage

}

});

我们通过 define方法定义一个 js模块,并通过 return对外暴露出接口(两个属性,一个方法)。同时该模块也是依赖于 jQuery。

四、require.configh 函数配置说明

要改变 RequireJS的默认配置,可以使用 require.configh函数传入一个可选参数对象。上面只演示了其中 baseUrl和 paths这两个配置,下面是一个完整的配置:

require.config({

baseUrl: 'js',

paths: {

jquery: 'lib/jquery-1.11.1'

},

shim: {

'backbone': {

deps: ['underscore', 'jquery'],

exports: 'Backbone'

},

'underscore': {

exports: '_'

},

'modal':{//模态框插件不是模块化

deps:['jquery'],

export:"modal"

},

},

map: {

'script/newmodule': {

'foo': 'foo1.2'

},

'script/oldmodule': {

'foo': 'foo1.0'

}

},

config: {

'script/bar': {

size: 'large'

},

'script/baz': {

color: 'blue'

}

}

});

1,baseUrl

用于加载模块的根路径。在配置这个属性后,以后的文件都是在这个路径下查找内容了。

2,paths

用于一些常用库或者文件夹路径映射,方便后面使用,省得每次都要输入一长串路径。(js文件的后缀可以省略)

3,shim

虽然目前已经有一部分流行的函数库(比如 jQuery)符合 AMD规范,但还有很多库并不符合。shim就是为了加载这些非 AMD规范的 js,并解决其载入顺序的。

比如上面样例,我们想通过 RequireJS来使用 backbone,那么你就需要在配置中把它定义为一个 shim。同时通过 deps配置其依赖关系,可以保证 underscore、jquery先被加载。

shim配置的注意事项:

shim配置仅设置了代码的依赖关系,想要实际加载 shim指定的或涉及的模块,仍然需要一个常规的 require/define调用。设置 shim本身不会触发代码的加载。

请仅使用其他"shim"模块作为 shim脚本的依赖,或那些没有依赖关系,并且在调用 define()之前定义了全局变量(如 jQuery或 lodash)的 AMD库。否则,如果你使用了一个 AMD模块作为一个 shim配置模块的依赖,在 build之后,AMD模块可能在 shim 托管代码执行之前都不会被执行,这会导致错误。终极的解决方案是将所有 shim托管代码都升级为含有可选的 AMD define()调用。

4,map

(1)对于给定的模块前缀,使用一个不同的模块 ID来加载该模块。该手段对于某些大型项目很重要。

比如上面配置以后,不同的模块会使用不同版本的"foo":

当some/newmodule 调用了 require('foo'),它将获取到 foo1.2.js文件。

当 some/oldmodule 调用 require('foo'),时它将获取到 foo1.0.js文件。

(2)map还支持“*”,意思是“对于所有的模块加载,使用本 map配置”。如果还有更细化的 map配置,会优先于“*”配置。

比如下面配置,除了“some/oldmodule”外的所有模块,当要用“foo”时,都使用“foo1.2”来替代。

requirejs.config({

map: {

'*': {

'foo': 'foo1.2'

},

'some/oldmodule': {

'foo': 'foo1.0'

}

}

});

5,config

我们需要将配置信息传给一个模块。这些配置往往是 application级别的信息,需要一个手段将它们向下传递给模块。这个通过requirejs.config()的 config配置项就可以实现。

(1)可以通过加载特殊的依赖“module”来获取这些信息。

// script/info.js

define(['module'], function (module) {

var color = module.config().color; //blue

});

(2)也可通过符合 CommonJS规范的模块获取

// script/info.js

define(function (require, exports, module) {

var color = module.config().color; //blue

});

五、不同类型的模块定义

模块不同于传统的脚本文件,它良好地定义了一个作用域来避免全局名称空间污染。

它可以显式地列出其依赖关系,并以函数(定义此模块的那个函数)参数的形式将这些依赖进行注入,而无需引用全局变量。同时因为无需创建全局变量,甚至可以做到在同一个页面上同时加载同一模块的不同版本。

RequireJS的模块语法允许它尽快地加载多个模块,虽然加载的顺序不定,但依赖的顺序最终是正确的。

一个磁盘文件应该只定义1 个模块。多个模块可以使用内置优化工具将其组织打包。

1,简单的键值对模块

(1)如果一个模块仅含键值对,没有任何依赖,则在define()中定义这些值对就好了

// script/shirt.js

define({

color: "black",

size: "unisize"

});

下面调用并测试这个模块

require(['script/shirt'],function (shirt) {

console.log("颜色:" + shirt.color);

});

a541ad19eaa0a03ea60745c0088641e1.png

(2)下面还是一个简单的键值对模块,没有任何依赖,但需要做一些初始化 setup工作。

// script/shirt.js

define(function () {

//在这里做一些setup工作

console.log("Do setup work here...");

return {

color: "black",

size: "unisize"

}

});

2,只有一个主函数的模块

(1)没有任何依赖

// script/info.js

define(function () {

return function (){

alert("欢迎访问 hangge.com");

};

});

下面调用并测试这个模块:

require(['script/info'],function (info) {

info();

});

(2)存在相关依赖

// script/info.js

define(['jquery'],function($){

return function (){

$('#messageBox').html('欢迎访问 hangge.com');

};

});

下面调用并测试这个模块:

require(['script/info'],function (info) {

info();

});

3,包含多个函数方法和变量的模块

// script/hello.js

define(['jquery'],function($){

//变量定义区

var moduleName = "hello module";

var moduleVersion = "1.0";

//函数定义区

var showMessage = function(name){

if(undefined === name){

return;

}else{

$('#messageBox').html('欢迎访问 ' + name);

}

};

//暴露(返回)本模块API

return {

"moduleName":moduleName,

"version": moduleVersion,

"showMessage": showMessage

}

});

下面调用并测试这个模块:

require(['jquery', 'script/hello'],function ($, hello) {

$("#btn").click(function(){

hello.showMessage("hangge.com");

});

});

六 、相对路径的规则

不管是在配置中写路径还是直接在 require函数中写路径,我们需要了解 requireJS在不同情况下的相对路径。以下是相对路径的规则:

如果

标签引入require.js时没有指定 data-main 属性,则以引入该 js的 html文件所在的路径为根路径。

如果有指定data-main属性,也就是有指定入口文件,则以入口文件所在的路径为根路径。

如果在 require.config()中有配置 baseUrl,则以 baseUrl的路径为根路径。

以上三条优先级逐级提升,如果有重叠,后面的根路径覆盖前面的根路径。

七、循环依赖问题解决

1,什么是循环依赖

(1)假设我们有如下 a、b 两个互相依赖的模块(a 依赖 b,b 同时依赖 a)

--- a.js ---

// script/a.js

define(['script/b'],function(b){

//函数定义区

var showMessage = function(){

console.log("欢迎访问 hangge.com" + b.getName());

};

//暴露(返回)本模块API

return {

"showMessage": showMessage

}

});

--- b.js ---

// script/a.js

define(['script/a'],function(a){

//函数定义区

var doSomething = function(){

a.showMessage();

};

var getName = function(){

return "hangge.com";

};

//暴露(返回)本模块API

return {

"doSomething": doSomething,

"getName": getName

}

});

(2)我们如果调用 b模块的 doSomething方法

require(['script/b'],function (b) {

b.doSomething();

});

发现b调用 a 正常,但是a 中调用 b 方法会报 undefined错误。

e387619b41e6c7aa3cdcee2d5de35dde.png

2,问题解决

循环依赖比较罕见,它也是一个重构代码重新设计的警示灯。但不管怎样,有时候还是要用到循环依赖。对于循环依赖,只要依赖环中任何一条边是运行时依赖,这个环理论上就是活的。而如果全部边都是装载时依赖,这个环就是死的。

为避免循环依赖引发的问题,其实只要把一边改成运行时依赖就可以了,我们有如下几种方法。

方法1:使用 require() 去获取一个模块

我们对模块 a 进行如下修改,即不再依赖前置加载。而是通过引入 require依赖,然后再通过require() 方法去载入模块 b,并在回调中去执行。

// script/a.js

define(['require'],function(require){

//函数定义区

var showMessage = function(){

require(['script/b'],function (b) {

console.log("欢迎访问 " + b.getName());

});

};

//暴露(返回)本模块API

return {

"showMessage": showMessage

}

});

再次调用b 模块的 doSomething方法,可以发现运行成功了:

baed8c6da39223703e3e9b84bb72e3e5.png

方法2:通过注入 exports 来解决

我们将模块 b修改成如下代码:

// script/b.js

define(['script/a', 'exports'],function(a, exports){

//函数定义区

var doSomething = function(){

a.showMessage();

};

var getName = function(){

return "hangge.com";

};

//暴露本模块API

exports.doSomething = doSomething;

exports.getName = getName;

});

方法3:直接使用 CommonJS 规范定义模块

我们将模块 b 修改成如下代码:

// script/b.js

define(function(require, exports, module) {

var a = require("script/a");

//函数定义区

var doSomething = function(){

a.showMessage();

};

var getName = function(){

return "hangge.com";

};

//暴露本模块API

exports.doSomething = doSomething;

exports.getName = getName;

});

八、错误处理

一般我们使用 RequireJS时碰到的错误主要是 404(未找到)错误、网络超时或加载的脚本含有错误。RequireJS提供了如下三种方式来处理这些错误。

1,require 的错误回调函数

下面代码先用 jQuery库的一个 CDN版本,如果其加载出错,则切换到本地版本。

require.config({

//如果你打算支持Internet Explorer捕获加载错,并使用了define()或shim,则要将enforceDefine设置为true。

enforceDefine: true,

paths: {

jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'

}

});

require(['jquery'], function ($) {

//Do something with $ here

}, function (err) {

//获取加载错误的模块

var failedId = err.requireModules && err.requireModules[0];

if (failedId === 'jquery') {

//通过undef方法让所有依赖jQuery的模块都暂不加载,直到正确的jQuery被加载。

requirejs.undef(failedId);

//将jQuery设置为本地路径

requirejs.config({

paths: {

jquery: 'local/jquery'

}

});

//再次尝试加载jQuery

require(['jquery'], function () {});

} else {

//处理其他模块错误

}

});

2,通过"paths"数组配置

paths配置项允许我们使用数组,下面代码先尝试加载 CDN版本,如果出错,则退回到本地的 lib/jquery.js。

requirejs.config({

//如果你打算支持Internet Explorer捕获加载错,并使用了define()或shim,则要将enforceDefine设置为true。

enforceDefine: true,

paths: {

jquery: [

'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min',

//如果CDN文件加载失败,则自动加载本地版本

'lib/jquery'

]

}

});

//Later

require(['jquery'], function ($) {

});

3,全局的错误捕获:require.onError

为了捕获在局域的 errback中未捕获的异常,可以重载 require.onError()来实现全局的异常捕获。

require.onError = function (err) {

console.log(err.requireType);

if (err.requireType === 'timeout') {

console.log('modules: ' + err.requireModules);

}

throw err;

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值