RequireJs和AMD规范模块化编程相关笔记
RequireJS简介
RequireJS是一个JavaScript模块加载器。它非常适合在浏览器中使用。它是一个工具库,主要用于客户端的模块管理,当然也可以应用于服务器端,例如Node.js中。RequireJS模块管理遵循AMD规范(Asynchronoous Module Definetion)
RequireJS的使用和模块的定义
引入require.js
在模块化编程之前,我们需要事先将require.js嵌入网页中,这样就能开始在网页中进行模块化编程了
- RequireJS下载地址:http://requirejs.org/
- 引入首页面:(data-main属性指定主代码所在的脚本文件,也就是从哪里开始,项目所有javascript的入口文件,该属性不可以省略)这里js文件夹下面的main.js是用户指定的入口文件。
Require采用AMD规范实现
AMD通过将模块的实现代码包在匿名函数(即AMD的工厂方法,factory)中实现作用域的隔离,通过文件路径作为天然的模块ID实现命名空间的控制,将模块的工厂方法作为参数传入全局的define(由模块加载器事先定义),使得工厂方法的执行时机可控,也就变相模拟出了同步的局部require,因而AMD的模块可以不经转换地直接在浏览器中执行。
define方法定义模块
define方法用于定义一个模块,对于RequireJS来说,要求每一个模块都放在一个独立的文件里面,按照是否依赖其它的模块,定义的模块分为两种:
定义独立模块(独立模块不需要依赖于其它的模块)
//一个独立模块,不需要依赖任何其它模块,可以直接使用define方法生成 //写法一:定义一个拥有两个方法的模块,define里面定义一个拥有方法的对象 define({ method1:function(){}, method2:function(){} }); //写法二:定义一个拥有两个方法的模块,把对象写成一个函数,函数返回值就是输出的模块,define里面定义一个返回模块的函数function,这种自由度更高,可以在函数里面写一些初始化方法 define(function(){ return { method1:function(){}, method2:function(){} } }); //define定义的模块可以返回任何的值,不限于对象
定义非独立的模块(依赖其它的模块,定义模块必须采用如下进行定义)
//如果是非独立模块,必须采用如下进行定义 define(['module1','module2','module3'],function(mu1,mu2,mu3){ return { method:function(){ mu1.method1(); mu2.method2(); mu3.method3(); } } }) //define方法的第一个参数是一个数组,里面是当前模块所依赖的模块,上面代码中表示当前模块依赖3个模块,只有先加载这三个模块,当前模块才能正常的运行。 //一般情况下,module1,module2,module3是指当前目录下面的module1.js和module2.js和module3.js文件['module1','module2','module3']等价于['./module1','./module2','./module3'] //define方法的第二个参数是一个函数,当前面的所有成员加载成功以后,该函数将会被调用,函数参数与数组成员一一对应,函数必须返回一个对象,以供其它模块的调用。 //上面的代码表示模块返回一个对象,该对象的method方法就是外部调用的接口,method方法内调用了module1、module2、module3的方法。 //如果一个模块的依赖模块过多,参数与模块一一对应非常麻烦,为了避免繁琐的写法,RequireJS提供一种更加简单的定义模块的方法 define(function(require){ var module1 = require("module1"); module2 = require("module2"); module3 = require("module3"); module4 = require("module4"); ....... });
require方法:调用模块
require方法用于调用模块,参数与define方法类似
require(['module1','module2'],function(module1,module2){
//调用方法
});
//上面方法表示加载两个模块,如果模块都加载成功以后,就执行回掉方法
require的第一个参数就是表示一个依赖关系的数组,这个数组可以写的很灵和
require([window.JSON ? undefine:'util/json2'],function(json){
JSON = JSON || window.JSON;
console.log(JSON.parse('{"JSON":"HERE"}'));
});
//上面的代码就写的很灵活,首先判定浏览器是否支持原生JSON如果支持,传入undefine否则就是用工具包util目录下面的json2模块
require方法可以用在define方法内部
define(function(require){
var module = require("module");
});
//动态加载模块
define(function(require){
var isReady = false,foobar;
require(["module1","module2"],function(module1,module2){
isReady=true;
foobar = module1() + module2();
});
return {
isReady:isReady,
foobar:foobar
}
});
一个输出Promise对象的例子,可以在该对象的then方法指定下一个动作
require(['lib/Deferred'],function(Deferred){
var defer = new Dferred();
require(['lib/templates/?index.html','lib/data/?stats'],function(templates,data){
//成功后,通过defer来处理,调用defer.resolve后,Promise的状态变为fullfilled已处理
defer.resolve({template:template,data:data});
});
return defer.promise();
});
require方法允许第三个参数,即处理错误的回掉函数,接收一个error对象作为参数
require(["module"],function(module){
//加载成功的时候回掉函数
},function(error){
//错误回掉函数
});
require对象还允许指定一个全局的Error事件监听函数,所有没有被上面的方法捕获的错误,都会触发onError
requirejs.onError = function(){
//......
}
配置require.js:config方法
require方法本身也是一个对象,拥有一个config方法,用来配置require.js运行参数。config方法接收一个对象作为参数。
//require的config方法接收一个对象作为参数
require.config({
paths:{
jquery:[
'//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js',
'lib/jquery.min.js'
]
}
});
config方法参数对象的属性如下:
paths属性
paths参数指定各个模块的位置,位置可以是同一个服务器上的相对位置,也可以是外部网址。可以为每个模块定义多个位置,如果第一个位置加载失败,则加载第二个位置,上面的示例就表示如果CDN加载失败,则加载服务器上的备用脚本。需要注意的是,指定本地文件路径时,可以省略文件最后的js后缀名。
//加载jquery模块,因为jquery的路径已经在paths参数中定义了,所以会到事先设定的位置下载
require(["jquery"], function($) {
// ...
});
baseUrl属性
baseUrl参数指定本地模块位置的基准目录,也就是本地模块路径相对于哪一个目录。该属性通常由require.js加载时的data-main属性指定。
shim属性
有些库不是AMD兼容的,这个时候就需要指定shim属性的值。shim可以理解为”垫片”,帮助require.js加载非AMD规范的库。
require.config({
paths:{
"backbone":"vendor/backbone",
"underscore":"vendor/underscore"
},
shim:{
"backbone":{
//指定依赖
deps:["underscore"],
//指定输出符号
exports:"Backbone"
},
"underscore":{
//指定输出符号
exports:"_"
}
}
});
//上面代码中的backbone和underscore就是非AMD规范的库。shim指定它们的依赖关系(backbone依赖于underscore),以及输出符号(backbone为“Backbone”,underscore为“_”)。
requirejs插件
RequireJS允许使用插件,加载各种格式的数据。
插入文本数据使用的text插件例子
define(['module1','text!templates.html'],function(module1,templates){
});
//上面代码加载的第一个模块是module1,第二个模块是一个文本,用'text!'表示。该文本作为字符串存放在回掉函数的templates变量中。
优化器 r.js
RequireJS提供一个基于node.js的命令行工具r.js,用来压缩多个js文件。它的主要作用是将多个模块文件压缩合并成一个脚本文件,以减少网页的HTTP请求数。
使用步骤:
第一步安装r.js(如果已经安装node.js):运行 npm install -g requirejs
然后使用命令:node r.js -o (arguments表示命令运行的时候所需要的一系列参数,例如)
node r.js -o baseUrl= . name=main out=main-build.js
除了命令行提供参数设置,也可以将参数写入一个文件,假定文件名称为build.js
({
baseUrl:".",
name:"main",
out:"main-build.js"
})
然后在用r.js运行这个参数文件,就可以了不需要其它步骤了。
node r.js -o build.js
下面是一个范例,位置在根目录下面,文件名为build.js:
({
appDir: './',
baseUrl: './js',
dir: './dist',
modules: [
{
name: 'main'
}
],
fileExclusionRegExp: /^(r|build)\.js$/,
optimizeCss: 'standard',
removeCombined: true,
paths: {
jquery: 'lib/jquery',
underscore: 'lib/underscore',
backbone: 'lib/backbone/backbone',
backboneLocalstorage: 'lib/backbone/backbone.localStorage',
text: 'lib/require/text'
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
},
backboneLocalstorage: {
deps: ['backbone'],
exports: 'Store'
}
}
})
上面代码将多个模块压缩为一个main.js
文件配置属性解释:
- appDir:项目目录,相对于参数文件的位置。
- baseUrl:js文件的位置。
- dir:输出目录。
- modules:一个包含对象的数组,每个对象就是一个要被优化的模块。
- fileExclusionRegExp:凡是匹配这个正则表达式的文件名,都不会被拷贝到输出目录。
- optimizeCss: 自动压缩CSS文件,可取的值包括“none”, “standard”, “standard.keepLines”, “standard.keepComments”, “standard.keepComments.keepLines”。
- removeCombined:如果为true,合并后的原文件将不保留在输出目录中。
- paths:各个模块的相对路径,可以省略js后缀名。
- shim:配置依赖性关系。如果某一个模块不是AMD模式定义的,就可以用shim属性指定模块的依赖性关系和输出值。
generateSourceMaps:是否要生成source map文件。
更多信息看官方文档https://github.com/requirejs/r.js/blob/master/build/example.build.js
另一个build.js的例子
({
mainConfigFile : "js/main.js",
baseUrl: "js",
removeCombined: true,
findNestedDependencies: true,
dir: "dist",
modules: [
{
name: "main",
exclude: [
"infrastructure"
]
},
{
name: "infrastructure"
}
]
})
上面代码将模块文件压缩合并成两个文件,第一个是main.js(指定排除infrastructure.js),第二个则是infrastructure.js。
来自 《JavaScript标准参考教程(alpha)》 by阮一峰