RequireJS和AMD规范
目录
概述
RequireJS是一个工具库,主要用于客户端的模块管理。它可以让客户端的代码分成一个个模块,实现异步或动态加载,从而提高代码的性能和可维护性。它的模块管理遵守AMD规范(Asynchronous Module Definition)。
RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。
首先,将require.js嵌入网页,然后就能在网页中进行模块化编程了。
<script data-main="scripts/main" src="scripts/require.js"></script>
上面代码的data-main属性不可省略,用于指定主代码所在的脚本文件,在上例中为scripts子目录下的main.js文件。用户自定义的代码就放在这个main.js文件中。
define方法:定义模块
define方法用于定义模块,RequireJS要求每个模块放在一个单独的文件里。
按照是否依赖其他模块,可以分成两种情况讨论。第一种情况是定义独立模块,即所定义的模块不依赖其他模块;第二种情况是定义非独立模块,即所定义的模块依赖于其他模块。
(1)独立模块
如果被定义的模块是一个独立模块,不需要依赖任何其他模块,可以直接用define方法生成。
define({
method1: function() {},
method2: function() {},
});
上面代码生成了一个拥有method1、method2两个方法的模块。
另一种等价的写法是,把对象写成一个函数,该函数的返回值就是输出的模块。
define(function () {
return {
method1: function() {},
method2: function() {},
};
});
后一种写法的自由度更高一点,可以在函数体内写一些模块初始化代码。
值得指出的是,define定义的模块可以返回任何值,不限于对象。
(2)非独立模块
如果被定义的模块需要依赖其他模块,则define方法必须采用下面的格式。
define(['module1', 'module2'], function(m1, m2) {
...
});
define方法的第一个参数是一个数组,它的成员是当前模块所依赖的模块。比如,['module1', 'module2']表示我们定义的这个新模块依赖于module1模块和module2模块,只有先加载这两个模块,新模块才能正常运行。一般情况下,module1模块和module2模块指的是,当前目录下的module1.js文件和module2.js文件,等同于写成['./module1', './module2']。
define方法的第二个参数是一个函数,当前面数组的所有成员加载成功后,它将被调用。它的参数与数组的成员一一对应,比如function(m1, m2)就表示,这个函数的第一个参数m1对应module1模块,第二个参数m2对应module2模块。这个函数必须返回一个对象,供其他模块调用。
define(['module1', 'module2'], function(m1, m2) {
return {
method: function() {
m1.methodA();
m2.methodB();
}
};
});
上面代码表示新模块返回一个对象,该对象的method方法就是外部调用的接口,menthod方法内部调用了m1模块的methodA方法和m2模块的methodB方法。
需要注意的是,回调函数必须返回一个对象,这个对象就是你定义的模块。
如果依赖的模块很多,参数与模块一一对应的写法非常麻烦。
define(
[ 'dep1', 'dep2', 'dep3', 'dep4', 'dep5', 'dep6', 'dep7', 'dep8'],
function(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8){
...
}
);
为了避免像上面代码那样繁琐的写法,RequireJS提供一种更简单的写法。
define(
function (require) {
var dep1 = require('dep1'),
dep2 = require('dep2'),
dep3 = require('dep3'),
dep4 = require('dep4'),
dep5 = require('dep5'),
dep6 = require('dep6'),
dep7 = require('dep7'),
dep8 = require('dep8');
...
}
});
下面是一个define实际运用的例子。
define(['math', 'graph'],
function ( math, graph ) {
return {
plot: function(x, y){
return graph.drawPie(math.randomGrid(x,y));
}
}
};
);
上面代码定义的模块依赖math和graph两个库,然后返回一个具有plot接口的对象。
另一个实际的例子是,通过判断浏览器是否为IE,而选择加载zepto或jQuery。
define(('__proto__' in {} ? ['zepto'] : ['jquery']), function($) {
return $;
});
上面代码定义了一个中间模块,该模块先判断浏览器是否支持__proto__属性(除了IE,其他浏览器都支持),如果返回true,就加载zepto库,否则加载jQuery库。
require方法:调用模块
require方法用于调用模块。它的参数与define方法类似。
require(['foo', 'bar'], function ( foo, bar ) {
foo.doSomething();
});
上面方法表示加载foo和bar两个模块,当这两个模块都加载成功后,执行一个回调函数。该回调函数就用来完成具体的任务。
require方法的第一个参数,是一个表示依赖关系的数组。这个数组可以写得很灵活,请看下面的例子。
require( [ window.JSON ? undefined : 'util/json2' ], function ( JSON ) {
JSON = JSON || window.JSON;
console.log( JSON.parse( '{ "JSON" : "HERE" }' ) );
});
上面代码加载JSON模块时,首先判断浏览器是否原生支持JSON对象。如果是的,则将undefined传入回调函数,否则加载util目录下的json2模块。
require方法也可以用在define方法内部。
define(function (require) {
var otherModule = require('otherModule');
});
下面的例子显示了如何动态加载模块。
define(function ( require ) {
var isReady = false, foobar;
require(['foo', 'bar'], function (foo, bar) {
isReady = true;
foobar = foo() + bar();
});
return {
isReady: isReady,
foobar: foobar
};
});
上面代码所定义的模块,内部加载了foo和bar两个模块,在没有加载完成前,isReady属性值为false,加载完成后就变成了true。因此,可以根据isReady属性的值,决定下一步的动作。
下面的例子是模块的输出结果是一个promise对象。
define(['lib/Deferred'], function( Deferred ){
var defer = new Deferred();
require(['lib/templates/?index.html','lib/data/?stats'],
function( template, data ){
defer.resolve({ template: template, data:data });
}
);
return defer.promise();
});
上面代码的define方法返回一个promise对象,可以在该对象的then方法,指定下一步的动作。
如果服务器端采用JSONP模式,则可以直接在require中调用,方法是指定JSONP的callback参数为define。
require( [
"http://someapi.com/foo?callback=define"
], function (data) {
console.log(data);
});
require方法允许添加第三个参数,即错误处理的回调函数。
require(
[ "backbone" ],
function ( Backbone ) {
return Backbone.View.extend({ /* ... */ });
},
function (err) {
// ...
}
);
require方法的第三个参数,即处理错误的回调函数,接受一个error对象作为参数。
require对象还允许指定一个全局性的Error事件的监听函数。所有没有被上面的方法捕获的错误,都会被触发这个监听函数。
requirejs.onError = function (err) {
// ...
};
AMD模式小结
define和require这两个定义模块、调用模块的方法,合称为AMD模式。它的模块定义的方法非常清晰,不会污染全局环境,能够清楚地显示依赖关系。
AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块。
配置require.js:config方法
require方法本身也是一个对象,它带有一个config方法,用来配置require.js运行参数。config方法接受一个对象作为参数。
require.config({
paths: {
jquery: [
'//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js',
'lib/jquery'
]
}
});
config方法的参数对象有以下主要成员:
(1)paths
paths参数指定各个模块的位置。这个位置可以是同一个服务器上的相对位置,也可以是外部网址。可以为每个模块定义多个位置,如果第一个位置加载失败,则加载第二个位置,上面的示例就表示如果CDN加载失败,则加载服务器上的备用脚本。需要注意的是,指定本地文件路径时,可以省略文件最后的js后缀名。
require(["jquery"], function($) {
// ...
});
上面代码加载jquery模块,因为jquery的路径已经在paths参数中定义了,所以就会到事先设定的位置下载。
(2)baseUrl
baseUrl参数指定本地模块位置的基准目录,即本地模块的路径是相对于哪个目录的。该属性通常由require.js加载时的data-main属性指定。
(3)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允许使用插件,加载各种格式的数据。完整的插件清单可以查看官方网站。
下面是插入文本数据所使用的text插件的例子。
define([
'backbone',
'text!templates.html'
], function( Backbone, template ){
// ...
});
上面代码加载的第一个模块是backbone,第二个模块则是一个文本,用'text!'表示。该文本作为字符串,存放在回调函数的template变量中。
优化器r.js
RequireJS提供一个基于node.js的命令行工具r.js,用来压缩多个js文件。它的主要作用是将多个模块文件压缩合并成一个脚本文件,以减少网页的HTTP请求数。
第一步是安装r.js(假设已经安装了node.js)。
npm install -g requirejs
然后,使用的时候,直接在命令行键入以下格式的命令。
node r.js -o <arguments>
<argument>表示命令运行时,所需要的一系列参数,比如像下面这样:
node r.js -o baseUrl=. name=main out=main-built.js
除了直接在命令行提供参数设置,也可以将参数写入一个文件,假定文件名为build.js。
({
baseUrl: ".",
name: "main",
out: "main-built.js"
})
然后,在命令行下用r.js运行这个参数文件,就OK了,不需要其他步骤了。
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文件。
更详细的解释可以参考官方文档。
运行优化命令后,可以前往dist目录查看优化后的文件。
下面是另一个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。
参考链接
- NaorYe, Optimize (Concatenate and Minify) RequireJS Projects
- Jonathan Creamer, Deep dive into Require.js
- Addy Osmani, Writing Modular JavaScript With AMD, CommonJS & ES Harmony
- Jim Cowart, Five Helpful Tips When Using RequireJS
- Jim Cowart, Using r.js to Optimize Your RequireJS Project
RequireJS和AMD规范的更多相关文章
- Requirejs之AMD规范
一.什么是AMD规范 AMD是Asynchronous Module Definition-----异步模块定义 AMD规范定义了2个函数define()与require() 下面我们来看一下定义方法 ...
- JavaScript模块化-RequireJs实现AMD规范的简单例子
AMD规范简介 AMD(异步模块定义),是实现JavaScript模块化规范之一,它采用异步方式加载模块,模块的加载不影响后面语句的运行.require.js和curl.js都是实现AMD规范的优秀加 ...
- JavaSript模块规范 - AMD规范与CMD规范介绍(转)
JavaSript模块规范 - AMD规范与CMD规范介绍 JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发? 模块化是指在解决某一个复杂问题或者 ...
- JavaSript模块规范 - AMD规范与CMD规范介绍 (转载lovenyf.blog.chinaunix.net)
JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发? 模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题 ...
- JavaScript模块化---AMD规范
JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发? 模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问 题进行系 ...
- 【转】JavaSript模块规范 - AMD规范与CMD规范介绍
JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发? 模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题 ...
- JavaSript模块规范 - AMD规范与CMD规范介绍[转]
原文地址:http://blog.chinaunix.net/uid-26672038-id-4112229.html JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什 ...
- JavaSript模块规范 - AMD规范与CMD规范介绍 (转)
JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发? 模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题 ...
- 详解AMD规范及具体实现requireJS在工程中的使用
前面的话 由CommonJS组织提出了许多新的JavaScript架构方案和标准,希望能为前端开发提供统一的指引.AMD规范就是其中比较著名一个,全称是Asynchronous Module Defi ...
随机推荐
- Isilon OneFS Simulator Install Guide
Isilon build for storage data Use VMware converter to convert node1 to ESX(参考silon_OneFS_Simulator_I ...
- JQuery基础教程:选择元素(下)
DOM遍历方法 利用前面介绍的jQuery选择符取得一组元素,就像是我们在DOM树中纵横遍历再经过筛选得到的结果一样.如果只有这一种取得元素的方式,那我们选择的余地从某个角度讲也是很有限的.很多情 ...
- 《Code Complete》ch.18 表驱动法
是什么 一种scheme,用表来做信息存取,代替逻辑语句(if/else) 为什么 简化逻辑语句,避免大量嵌套的 if/else 或者 switch/case 怎么用 三种访问表的方式 直接访问:将源 ...
- 关闭linux下的使用的端口
lsof -P -n -i kill pid 另外防火墙 iptables之类的也可以关闭端口
- sort函数CCF
题是比较水的啦,但是好久没有用sort函数啦,有点生疏^...^ #include<stdio.h> #include<string.h> #include<iostre ...
- 002 The Variables In Csharp
在介绍本章之前,我们先看一下C#的编译过程,如下图所示: 图片摘自:http://www.developingthefuture.net/compilation-process-and-jit-com ...
- GlusterFS特性介绍
下面是GlusterFS的一些特性 规范的接口 GlusterFS服务器与POSIX兼容,使用支持文件扩展属性的磁盘文件系统(如ext4.XFS)来存储磁盘上的数据.同时,可以通过业界标准的访问协议如 ...
- Swift开发中的一些琐碎
1.Swift中使用OC 1.创建 pch 文件,直接引用需要的头文件 #import"SQLite3.h" 2.修改 pct 路径 ,如下图,就可以使用了 2. Swift 没 ...
- mysql中NULL和null的区别
接触php的web开发一段时间了,在进行数据库操作的时候经常会遇到一个问题,使得同一字段在页面显示时有3种类型NULL,null以及数字,当时的解决办法是将这一字段定义为varchar类型,在插入数据 ...
- bzoj1216 [HNOI2003]操作系统
1216: [HNOI2003]操作系统 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 751 Solved: 419[Submit][Status ...