1、综述:
RequireJS的目标是代码的模块化,它使用了不同于传统<script>标签的脚本加载步骤。
可以用它来加速代码加载、优化代码,但其主要目的还是为了代码的模块化。
2、加载js文件
RequireJS以一个相对于baseUrl的地址来加载所有的代码。 <script>标签含有一个特殊的属性data-main,require.js使用它来启动脚本加载过程,(其中data-main是入口,想当于java的main方法)而baseUrl一般设置到与该属性相一致的目录:
<script data-main="scripts/main.js" src="scripts/require.js"></script>
这样的设置,baseUrl指向到了scripts目录,当然baseUrl可以手动的指定,也就是requirejs.config({})配置,如果没有显式配置,baseUrl默认指向引用requirejs的html所在的目录。
3、例子解析,目录结构:
www/ -index.html -js/ ---app/ ------sub.js ---lib/ ------jquery.js ------canvas.js ---app.js
index.html中的片段
<script data-main="js/app.js" src="js/require.js"></script>
文件入口app.js
requirejs.config({ baseUrl: 'js/lib', paths: { app: '../app' } }); // Start the main app logic. requirejs(['jquery', 'canvas', 'app/sub'], function ($, canvas, sub) { //jquery,canvas,sub 资源都已经加载完毕,可以用了。 }); });
baseUrl为加载文件的起始位置,不过请注意的是paths配置(paths是路径相对于baseUrl而言的,加载的文件都不需要.js后缀,因为requirejs默认依赖资源为js文件,如果加上了后缀,则会加载报错),这里配置的意思是,如果以“app”开始的Module ID(baseUrl+paths),则js/app 加载。
4、requirejs核心
(1)定义模块
模块不同于传统的脚本文件,它良好地定义了一个作用域来避免全局名称空间污染。还可以显式地列出其依赖关系,并将其依赖注入进来。
定义简单的对象:
define({ name: "hcc0926", age: "3" });
定义一个方法,没有外部依赖:
define(function () { return { name: "hcc0926", age: "3" } });
定义一个方法,包含外部依赖:
先看个例子,该模块依赖bb.js 和 aa.js:
define(["./aa", "./bb"], function(aa, bb) { return { name: "hcc0926", age: "3", addToCart: function() { aa.xxx(); bb.add(); } } });
define中的一个参数是个数组,就依赖项,第二个参数是个function(即模块的定义是在该方法体中定义),等所有的依赖项加载完成后执行第二个参数function,依赖关系会以参数的形式注入到该函数上,参数列表与依赖名称列表一一对应(名称可以不一致)。如果你对js比较熟悉的话,就会明白,function中不止可以返回对象,还可以返回方法,即如下形式:
define(["./aa", "./bb"], function(aa, bb) { return function(){ return aa.xxx(); } });
当然你可以可以指定module名称:
define("moduleId",["aa", "bb"],function(aa, bb) { //Define module in here. });
(2) 包装成commonJs的风格
如果你现有一些以CommonJS模块格式编写的代码,而这些代码难于使用上述依赖名称数组参数的形式来重构,你可以考虑直接将这些依赖对应到一些本地变量中进行使用。你可以使用一个CommonJS的简单包装来实现:
define(function(require, exports, module) { var a = require('a'), b = require('b'); //Return the module value return function () {}; });
或者也可以写出下面的形式,通过exports把本模块中的接口暴露给外部,供其他地方调用
define(function(require, exports, module) { var a = require('a'),b = require('b'); exports.add = a.xxx()+b.xxx(); });
5、其他需要注意点
(1)一个文件定义一个模块
(2)define()中的相对模块名: 为了可以在define()内部使用诸如require("./relative/name")的调用以正确解析相对名称,记得将"require"本身作为一个依赖注入到模块中:
define(["require", "./relative/name"], function(require) { var mod = require("./relative/name"); });
这样看着也比较别扭,可以通过commonJS的风格来改造成下面的形式:
define(function(require) { var mod = require("./relative/name"); });
(3)生成相对于模块的URL地址:如果你想生成某个引用文件的url,可以使用以下形式:
define(["require"], function(require) { var cssUrl = require.toUrl("./style.css"); });
(4)加载非AMD规范的文档:
对于backbone,underscore,已经其他的非AMD规范的需要借助requirejs.config({})中的 shim:
requirejs.config({ baseUrl : {}, shim: { 'backbone': { deps: ['underscore', 'jquery'], //backbone的依赖库 exports: 'Backbone' //backbone对外暴露的接口 }, 'underscore': { exports: '_' } } }); //在其他的文件中就可以通过如下形式访问了, define(['backbone'], function (Backbone) { return Backbone.Model.extend({}); });
使用shim的注意点:
1-shim配置仅设置了代码的依赖关系,想要实际加载shim指定的或涉及的模块,仍然需要一 个常规的require/define调用。设置shim本身不会触发代码的加载。
2-请仅使用其他"shim"模块作为shim脚本的依赖
3-不要混用CDN加载和shim配置
(5)map: 对于给定的模块前缀,使用一个不同的模块ID来加载该模块。
requirejs.config({ map: { 'some/newmodule': { 'foo': 'foo1.2' }, 'some/oldmodule': { 'foo': 'foo1.0' } } });
(6)requirejs.config中的其他配置:
config:这些配置往往是application级别的信息,需要一个手段将它们向下传递给模 块
packages: 从CommonJS包(package)中加载模块。参见从包中加载模块。
还有context,xhtml,urlArgs,callback等,请参考 http://requirejs.org/docs/api.html
(7)多版本支持,加载插件,定义国际化组件
暂时没有使用过,后续讨论。
附一个比较好的amd教程
http://efe.baidu.com/blog/dissecting-amd-what/