AMD是比较著名的前端模块化规范之一,全称是Asynchronous Module Definition,即异步模块加载机制,完整描述了模块的定义,依赖关系,引用关系以及加载机制。异步强调的是,在加载模块以及模块所依赖的其它模块时,都采用异步加载的方式,避免模块加载阻塞了网页的渲染进度。RequireJS是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一。
1、引入requirejs
<script data-main="scripts/main" src="https://cdn.bootcss.com/require.js/2.3.6/require.js"></script>
// 此处再引入requirejs时,如果没有通过data-main设置主入口文件,则基础路径即为引入requirejs文件所在的文件的路径;如果设置了主入口文件,那么主入口文件的路径即基本路径。在main.js中所设置的脚本是异步加载的。所以如果在页面中配置了其它JS加载,则不能保证它们所依赖的JS已经加载成功
2、基本配置
require.config({
// 设置的基本路径:指定本地模块的基准目录,即本地模块的路径是相对于那个目录的,该属性通常由requireJS在页面加载时的data-main属性指定
"baseUrl": "/static/",
// paths参数: paths是映射那些不直接放在baseUrl指定的目录下的文件,设置paths的起始位置是相对于baseUrl的,除非该path设置是以”/”开头或含有URL协议(http://或者https://)。
"paths": {
"jquery" : ["https://cdn.staticfile.org/jquery/3.3.1/jquery", "lib/jquery"], // 一个模块可以是多个地址,如果加载出错会更换一个进行再次尝试
"lib" : "/js/lib" // 因为设置了baseUrl,所以这里的位置实际上是/static/js/lib
// config参数:config是指需要将配置信息传给一个模块,这些配置往往是application级别的信息,需要一个手段将他们向下传递给模块。在 requireJS中,基于requirejs.config()的config配置项来实现。要获取这些信息的模块可以加载特殊的依赖 ”moudle” ,并调用module.config()获取。
"config": {
"lib/moduleA": {
"label" : "moduleA"
}
},
// shim参数: 解决使用非AMD方式定义的模块(如jquery插件)及其载入顺序,为那些没有使用define()来声明依赖关系,设置模块的”浏览器全局变量注入”型脚本做依赖和导出配置。
shim:{
"BMap": {
// deps: ["jquery"],
exports: "BMap"
},
"MarkerClusterer":{
deps: ["async!BMap","TextIconOverlay"], // deps数组,表明该模块的依赖性
exports: "MarkerClusterer" // exports值(输出的变量名),表明这个模块外部调用时的名称
},
"TextIconOverlay":{
deps: ["async!BMap"],
exports: "TextIconOverlay"
}
},
// 资源后面加上参数,可以用来解决缓存问题
urlArgs: "v2",
// Map参数: Map参数是用来解决同一个模块不同版本的问题,比如在项目开发中,开发初期使用了jquery1.7版本,但是由于业务的需求需要引入 jquery1.9以上的版本时候,但是又担心有些是依赖于jquery1.7的代码升级到1.9以上的时候会有问题,因此可以让一部分代码还是依赖于 jquery1.7,薪增的代码依赖于jquery1.9。当“some/newmodule”调用了“require("foo")”,它将获取到jquery1.9文件;而当“some/oldmodule”调用“`require("foo")”时它将获取到jquery1.7。
map: {
"some/newmodule": {
"foo": "jquery1.9"
},
"some/oldmodule": {
"foo": "jquery1.7"
}
}
});
3、定义模块
define([module-name?], [array-of-dependencies?], [module-factory-or-object]);
module-name: 模块标识,可以省略
array-of-dependencies: 所依赖的模块,可以省略
module-factory-or-object: 模块的实现,或者一个JavaScript对象
define函数具有异步性。当define函数执行时,首先会异步的去调用第二个参数中列出的依赖模块,当所有的模块被载入完成之后,如果第三个参数是一个回调函数则执行;然后告诉系统模块可用,也就通知了依赖于自己的模块自己已经可用。
简单的值对
当所要定义的模块只是单纯的键值对形式数据,没有任何依赖也不具有任何的功能时可以直接将要返回的数据对象写在 define方法中。
define({
color: "black",
size: "unisize"
});
函数式定义
require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。
具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。
define(function(){
return {
color: "black",
size: "unisize"
};
});
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖。
define(["jquery"], function($){
// 有依赖关系的模块定义
});
虽然RequireJS 提倡依赖前置,但是现在 RequireJS 也支持就近依赖了,因此还可以通过以下方式来引入依赖文件。
define(function(require, exports, modules){
// require:加载模块时使用。
// exports:导出模块的返回值。
// modules:定义模块的相关信息以及参数。
var $ = require("jquery");
});
需要注意的是上面两种方式无法同时使用,除非提前在依赖列表里定义需要加载模块,但这样做实际上感觉多次一举。
define(["require", "dep1", "dep2"], function (require) {
var dep1 = require("dep1");
var dep2 = require("dep2");
});
define(["dep1", "dep2"], function () {
var dep1 = require("dep1");
});
4、引入模块
require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块;第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。
require(["lib/moduleA", "lib/moduleB"], function(moduleA, moduleB){
...
});
require(function(require, exports, module){
// CommonJs形式的模块定义
var moduleA = require("lib/moduleA");
});
5、require.js插件
require.js还提供一系列插件,实现一些特定的功能。
domready插件,可以让回调函数在页面DOM结构加载完成后再运行。
require(['domready!'], function (doc){
// called once the DOM is ready
});
text和image插件,则是允许require.js加载文本和图片文件。
define([
'text!review.txt',
'image!cat.jpg'
], function(review,cat){
console.log(review);
document.body.appendChild(cat);
}
);
类似的插件还有json和mdown,用于加载json文件和markdown文件。