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。

RequireJS和AMD规范

时间: 08-05

RequireJS和AMD规范的相关文章

使用requireJS加载不符合AMD规范的js文件:shim的使用方式和实现原理

我们知道在javascript中定义全局变量有2种方式,本质上是等价的,都是向window对象注入属性或者方法. // global.js var g_name = "aty"; window.g_age = 25; 当global.js加载的时候,浏览器的全局对象window就会多出2个属性:g_name和g_age. 我们编写一个js工具类或者是js框架,通常有2种方式: 方式1:dateUtil.js (function(window) { var DateUtils = {};

JavaSript模块规范 - AMD规范与CMD规范介绍

JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发? 模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理.模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式.可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在.对于软件行业来说:解耦软件系统的复杂性,使得不管多么大的系统,也可以将管理,开发,维护变得"有理可循".

amd规范

http://www.cnblogs.com/snandy/archive/2012/03/12/2390782.html AMD设计出一个简洁的写模块API: define(id?, dependencies?, factory); 其中: id: 模块标识,可以省略. dependencies: 所依赖的模块,可以省略. factory: 模块的实现,或者一个JavaScript对象. id遵循CommonJS Module Identifiers .dependencies元素的顺序和fa

AMD规范与CMD规范的区别是什么?

AMD规范与CMD规范的区别是什么?    在比较之前,我们得先来了解下什么是AMD规范?什么是CMD规范?当然先申明一下,我个人也是总结下而已,也是网上看到的资料,自己总结下或者可以说整理下而已,供大家更深入的了解!因为我们都知道 AMD规范:是 RequireJS 在推广过程中对模块定义的规范化产出的,而CMD规范:是SeaJS 在推广过程中对模块定义的规范化产出的. 什么是CMD规范?  在CMD中 一个模块就是一个文件,如下代码所示: //基本格式如:define(id, deps, f

JavaSript模块化-AMD规范与CMD规范

JavaScript模块化 在了解AMD,CMD规范前,先来简单地了解下什么是模块化,模块化开发. 模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理.模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式.可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在.对于软件行业来说:解耦软件系统的复杂性,使得不管多么大的系统,也可以将管理,开发,维护变得"有理可循". 一些对

Javascript模块化编程(二):AMD规范

作者: 阮一峰 日期: 2012年10月30日 这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块. 但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!考虑到Javascript模块现在还没有官方规范,这一点就更重要了. 目前,通行的Javascript模块规范共有两种

require.js的AMD规范详解

require.js使用简介 在web刚开始发展的蛮荒时代,一个页面中只需要加载一个或少量的js文件,不存在模块,也不存在冲突之类的问题,但随着web项目的发展,它越来越大,js文件动辄几十个,怎么加载就成为了一个问题,要为浏览器的性能考虑,还有各个js文件(模块)的依赖关系.require.js的出现就是为了解决这样的问题. 1.实现js文件的异步加载,避免网页失去响应. 2.管理模块之间的依赖性,便于代码的编写和维护. require.js加载 使用require.js的第一步,是先去官方网

Javascript模块化开发 – AMD规范

通行的Javascript模块规范共有两种:CommonJS和AMD. 2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程.这标志"Javascript模块化编程"正式诞生.因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限:但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程. node.js的模块系统,就是参照CommonJS规范实现的.在CommonJS中,有一个全局性

Javascript模块化编程(二):AMD规范(转)

这个系列的第一部分介绍了Javascript模块的基本写法,今天介绍如何规范地使用模块. (接上文) 七.模块的规范 先想一想,为什么模块很重要? 因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块. 但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!考虑到Javascript模块现在还没有官方规范,这一点就更重要了. 目前,通行的Javascript模块规范共有两种:CommonJS和AMD.我主要介绍AMD,但

关于commonjs,AMD,CMD之间的异同

1.简介 随着前端业务复杂度的增加,模块化成为一个大的趋势.而在ES6还未被浏览器所支持的情况下,commonjs作为ES6中标准模块加载方案,在客服端中的支持情况并不好,现在在客服端中有2中模块化的解决方案,CMD和AMD,他们的代表分别为seajs和requirejs.这篇文章主要介绍我对commonjs.AMD以及CMD的理解. 2.commonJS commonjs的目标是制定一个js模块化的标准,它的目标制定一个可以同时在客服端和服务端运行的模块.这些模块拥有自己独立的作用域,也可以向