PS:基于 AMD(Asynchronous Module Definition)的 JavaScript 设计已经在目前较为流行的前端框架中大行其道,jQuery、Dojo、MooTools、EmbedJS 等纷纷在其最新版本中加入了对 AMD 的支持。本文介绍的是另一款较为精简的 RequireJS 框架,既想使用 AMD 的特性又不想引入一个庞大的库的开发人员,不妨试试 RequireJS。RequireJS 可以帮助用户异步按需的加载 JavaScript 代码,并解决 JavaScript 模块间的依赖关系,提升了前端代码的整体质量和性能
-
AMD 介绍
前端开发在近一两年发展的非常快,JavaScript作为主流的开发语言得到了前所未有的热捧。大量的前端框架出现了,这些框架都在尝试着解决一 些前端开发中的共性问题,但是实现又不尽相同。在这个背景下,CommonJS社区诞生了,为了让前端框架发展的更加成熟,CommonJS鼓励开发人员 一起在社区里为一些完成特定功能的框架制定规范。 AMD(Asynchronous Module Definition) 就是其中的一个规范。
-
传统JavaScript代码的问题
让我们来看看一般情况下JavaScript代码是如何开发的:通过<script>标签来载入JavaScript文件,用全局变量 来区分不同的功能代码,全局变量之间的依赖关系需要显式的通过指定其加载顺序来解决,发布应用时要通过工具来压缩所有的JavaScript代码到一个文 件。当Web项目变得非常庞大,前端模块非常多的时候,手动管理这些全局变量间的依赖关系就变得很困难,这种做法显得非常的低效。
-
AMD (Asynchronous Module Definition) 的引入
从名称上看便知它是适合script tag的。也可以说AMD是专门为浏览器中JavaScript环境设计的规范。它吸取了CommonJS的一些优点,但又不照搬它的格式。开始AMD作为CommonJS的transport format存在,因无法与CommonJS开发者达成一致而独立出来。它有自己的wiki和讨论组
AMD提出了一种基于模块的异步加载JavaScript代码的机制,它推荐开发人员将JavaScript代码封装进一个个模块,对全局对象的依 赖变成了对其他模块的依赖,无须再声明一大堆的全局变量。通过延迟和按需加载来解决各个模块的依赖关系。模块化的JavaScript代码好处很明显,各 个功能组件的松耦合性可以极大的提升代码的复用性、可维护性。这种非阻塞式的并发式快速加载JavaScript代码,使Web页面上其他不依赖 JavaScript代码的UI元素,如图片、CSS以及其他DOM节点得以先加载完毕,Web页面加载速度更快,用户也得到更好的体验。
-
CommonJS的AMD规范中只定义了一个全局的方法,如清单1所示
define(id?, dependencies?, factory);
该方法用来定义一个 JavaScript 模块,开发人员可以用这个方法来将部分功能模块封装在这个 define 方法体内
- dependencies 是一个字符串 Array,表示该模块依赖的其他所有模块标识,模块依赖必须在真正执行具体的 factory 方法前解决,这些依赖对象加载执行以后的返回值,可以以默认的顺序作为 factory 方法的参数。dependencies 也是可选参数,当用户不提供该参数时,实现 AMD 的框架应提供默认值为 [“require”,”exports”,“module”]。
- factory 是一个用于执行改模块的方法,它可以使用前面 dependencies 里声明的其他依赖模块的返回值作为参数,若该方法有返回值,当该模块被其他模块依赖时,返回值就是该模块的输出。
CommonJS 在规范中并没有详细规定其他的方法,一些主要的 AMD 框架如 RequireJS、curl、bdload 等都实现了 define 方法,同时各个框架都有自己的补充使得其 API 更实用。
-
CommonJS的AMD规范中只定义了一个全局的方法,如清单1所示
-
AMD设计出一个简洁的写模块API:
define(id?, dependencies?, factory);
其中:
- id: 模块标识,可以省略。
- dependencies: 所依赖的模块,可以省略。
- factory: 模块的实现,或者一个JavaScript对象。
以下是使用AMD模式开发的简单三层结构(基础库/UI层/应用层):
-
定义无依赖的模块
(base.js)
define(function() { return { mix: function(source, target) { } }; });
-
定义有依赖的模块
(ui.js,page.js)
ui.jsdefine(['base'], function(base) { return { show: function() { // todo with module base } } });
page.js
define(['data', 'ui'], function(data, ui) { // init here });
-
定义数据对象模块 (data.js)
define({ users: [], members: [] });
-
具名模块
define('index', ['data','base'], function(data, base) { // todo });
具名模块多数时候是不推荐的,一般由打包工具合并多个模块到一个js文件中时使用。
-
包装模块
前面提到dependencies元素的顺序和factory一一对应,其实不太严谨。AMD开始为摆脱CommonJS的束缚,开创性的提出了自己的模块风格。但后来又做了妥协,兼容了 CommonJS Modules/Wrappings 。即又可以这样写
define(function(require, exports, module) { var base = require('base'); exports.show = function() { // todo with module base } });
不考虑多了一层函数外,格式和Node.js是一样的。使用require获取依赖模块,使用exports导出API
除了define外,AMD还保留一个关键字require。
require 作为规范保留的全局标识符,可以实现为module loader。也可以不实现。目前,实现AMD的库有RequireJS 、curl 、Dojo 、bdLoad、JSLocalnet 、Nodules 等。
也有很多库支持AMD规范,即将自己作为一个模块存在,如MooTools 、jQuery 、qwery 、bonzo 甚至还有 firebug
-
简单示例
...
main.js
require.config({ paths: { jquery: 'jquery-1.7.2' } }); require(['jquery'], function($) { alert($().jquery); });
main.js中就两个函数调用require.config和require。
-
require.config用来配置一些参数,它将影响到requirejs库的一些行为。require.config的参数是一个JS对象,常用的
配置
有baseUrl,paths等。 - 这里配置了paths参数,使用模块名“jquery”,其实际文件路径jquery-1.7.2.js(后缀.js可以省略)。
这里require函数的第一个参数是数组,数组中存放的是模块名(字符串类型),数组中的模块与回调函数的参数一一对应。这里的例子则只有一个模块“jquery”。
-
我们知道jQuery从1.7后开始支持AMD规范,即如果jQuery作为一个AMD模块运行时,它的模块名是“jquery”。注意“jquery”是固定的
,不能写“jQuery”或其它。 - 如果文件名“jquery-1.7.2.js”改为“jquery.js”就不必配置paths参数了。
- require.config中config可以省略
jQuery中的支持AMD代码如下
if ( typeof define === "function" && define.amd && define.amd.jQuery ) { define( "jquery", [], function () { return jQuery; } ); }
访问index.html,网络请求如下
我们看到除了require.js外main.js和jquery-1.7.2.js也请求下来了。而它们正是通过requirejs请求的 -
require.config用来配置一些参数,它将影响到requirejs库的一些行为。require.config的参数是一个JS对象,常用的
-
实例包下载
参考:
1.AMD和RequireJS初识—-优化Web应用前端(按需动态加载JS)
2.使用 RequireJS 优化 Web 应用前端