RequireJS& AMD规范

来源:互联网 发布:淘宝店铺登陆 编辑:程序博客网 时间:2024/05/13 02:49

RequireJS

使用 RequireJS 加载 JavaScript 文件,若有两个 JavaScript 文件 a.js 和 b.js,里面各自定义了 myFunctionA 和 myFunctionB 两个方法,通过下面这个方式可以用 RequireJS 来加载这两个文件,在 function 部分的代码可以引用这两个文件里的方法。

<span style="font-size:10px;"><script src="./js/require.js"></script><script>     require(["./js/a.js", "./js/b.js"], function() {      myFunctionA();      myFunctionB();     }); </script></span>
require 方法里的这个字符串数组参数可以允许不同的值,当字符串是以”.js”结尾,或者以”/”开头,或者就是一个 URL 时,RequireJS 会认为用户是在直接加载一个 JavaScript 文件,否则,当字符串是类似”my/module”的时候,它会认为这是一个模块,并且会以用户配置的 baseUrl 和 paths 来加载相应的模块所在的 JavaScript 文件



RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。

首先,将require.js嵌入网页,然后就能在网页中进行模块化编程了。


<scriptdata-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() {},
});

上面代码生成了一个拥有method1method2两个方法的模块。

另一种等价的写法是,把对象写成一个函数,该函数的返回值就是输出的模块

define(function () {
    return {
        method1: function() {},
        method2: function() {},
    };
});

后一种写法的自由度更高一点,可以在函数体内写一些模块初始化代码。

值得指出的是,define定义的模块可以返回任何值,不限于对象。

2)非独立模块

如果被定义的模块需要依赖其他模块,则define方法必须采用下面的格式。

define(['module1', 'module2'], function(m1, m2) {
   ...
});

module1模块和module2模块指的是,当前目录下的module1.js文件和module2.js文件,等同于写成['./module1', './module2']

define方法的第二个参数是一个函数,当前面数组的所有成员加载成功后,它将被调用。它的参数与数组的成员一一对应这个函数必须返回一个对象,供其他模块调用。

define(['module1', 'module2'], function(m1, m2) {
    return {
        method: function() {
           m1.methodA();
            m2.methodB();
        }
    };
});

上面代码表示新模块返回一个对象,该对象的method方法就是外部调用的接口,menthod方法内部调用了m1模块的methodA方法和m2模块的methodB方法。

需要注意的是,回调函数必须返回一个对象,这个对象就是你定义的模块。

require方法:调用模块

require方法用于调用模块。它的参数与define方法类似。

require(['foo', 'bar'], function ( foo, bar ) {
        foo.doSomething();
});

上面方法表示加载foobar两个模块,当这两个模块都加载成功后,执行一个回调函数。该回调函数就用来完成具体的任务。

require方法也可以用在define方法内部。

define(function (require) {
    var otherModule = require('otherModule');
});

What should you do with many dependencies:

To make this easier, and to make it easy to do a simple wrapping around CommonJS modules, this form of define is supported, sometimes referred to as "simplified CommonJS wrapping":

 

define(function (require) {

    var dependency1 = require('dependency1'),

        dependency2 = require('dependency2');

 

    return function () {};

});

 

The AMD loader will parse out the require('') calls by using Function.prototype.toString(), then internally convert the above define call into this:

 

define(['require', 'dependency1', 'dependency2'], function (require) {

    var dependency1 = require('dependency1'),

        dependency2 = require('dependency2');

 

return function () {};

});


AMD模式小结

definerequire这两个定义模块、调用模块的方法,合称为AMD模式。它的模块定义的方法非常清晰,不会污染全局环境,能够清楚地显示依赖关系。

AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块

require方法本身也是一个对象,它带有一个config方法,用来配置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方法的参数对象有以下主要成员:

1baseUrl

<script>

  require.config({

    baseUrl: "/another/path",

    paths: {

        "some""some/v1.0"

    },

    waitSeconds: 15

  });

  require( ["some/module""my/module""a.js""b.js"],

    function(someModule,    myModule) {

        //该函数会在上述所有的依赖加载完毕后调用。

        //注意该函数可在页面加载完毕前被调用。

        //本回调函数是可选的。

    }

  );

</script>

 

所有模块的查找根路径。所以上面的示例中,"my/module"的标签src值是"/another/path/my/module.js"。当加载纯.js文件(依赖字串以/开头,或者以.js结尾,或者含有协议)不会使用baseUrl。因此a.jsb.js都在包含上述代码段的HTML页面的同目录下加载。

如未显式设置baseUrl,则默认值是加载require.jsHTML所处的位置。如果用了data-main属性,则该路径就变成baseUrl

baseUrl可跟require.js页面处于不同的域下,RequireJS脚本的加载是跨域的。

2paths

path映射那些不直接放置于baseUrl下的模块名设置path时起始位置是相对于baseUrl,除非该path设置以"/"开头或含有URL协议(如http:)。在上述的配置下,"some/module"script标签src值是"/another/path/some/v1.0/module.js"

用于模块名的path不应含有.js后缀,因为一个path有可能映射到一个目录。

paths备错配置:

requirejs.config({

    paths: {

        jquery: [

'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min',

            //若CDN加载错,则从如下位置重试加载

            'lib/jquery'

        ]

    }

});

require(['jquery'], function ($) {

});

上述代码先尝试加载CDN版本,如果出错,则退回到本地的lib/jquery.js

3shim

有些库不是AMD兼容的,这时就需要指定shim属性的值。shim可以理解成“垫片”,用来帮助require.js加载非AMD规范的库。

require.config({
    paths: {
        "backbone": "vendor/backbone",
        "underscore": "vendor/underscore"
    },
    shim: {
        "backbone": {
            deps: [ "underscore" ],
            exports: "Backbone"
        },
        "underscore": {
            exports: "_"
        }
    }
});

上面代码中的backboneunderscore就是非AMD规范的库。shim指定它们的依赖关系(backbone依赖于underscore),以及输出符号(backbone为“Backbone”,underscore为“_”)。

(4)map

对于给定的模块前缀,使用一个不同的模块ID来加载该模块。

该手段对于某些大型项目很重要:如有两类模块需要使用不同版本的"foo",但它们之间仍需要一定的协同。

 

requirejs.config({

    map: {

        'some/newmodule': {

            'foo''foo1.2'

        },

        'some/oldmodule': {

            'foo''foo1.0'

        }

    }

});

如果各模块在磁盘上分布如下:

· foo1.0.js

· foo1.2.js

· some/

newmodule.js

oldmodule.js

当“some/newmodule”调用了“require('foo')”,它将获取到foo1.2.js文件;而当“some/oldmodule”调用“require('foo')”时它将获取到foo1.0.js。

该特性仅适用于那些调用了define()并将其注册为匿名模块的真正AMD模块脚本。并且,请在map配置中仅使用绝对模块ID,“../some/thing”之类的相对ID不能工作。

另外在map中支持“*”,意思是“对于所有的模块加载,使用本map配置”。

 

(5)Packages

RequireJS支持从CommonJS包结构中加载模块,但需要一些额外的配置。具体地,支持如下的CommonJS包特性:

 

1.一个包可以关联一个模块名/前缀。

2.package config可为特定的包指定下述属性:

name:包名(用于模块名/前缀映射)

location: 磁盘上的位置。位置是相对于配置中的baseUrl值,除非它们包含协议或以“/”开头。

main:当以“包名”发起require调用后,所应用的一个包内的模块。默认为“main”。

而下面的示例中使用了两个包,cartstore

· project-directory/

project.html

cart/

§ main.js

store/

§ main.js

§ util.js

main.js

require.js


main.js使用“packages”配置项来设置相对于require.js的各个包,此例中是源码包“cart”及“store”:

//main.js的内容

//传递一个config object到require

require.config({

   "packages": ["cart""store"]

});

 

require(["cart""store""store/util"],

function (cart,   store,   util) {

   //正常地使用模块

});

对“cart”的依赖请求会从scripts/cart/main.js中加载,因为“main”是RequireJS默认的包主模块。对“store/util”的依赖请求会从scripts/store/util.js加载。

 

(6)deps

指定要加载的一个依赖数组。当将require设置为一个config object在加载require.js之前使用时很有用。一旦require.js被定义,这些依赖就已加载。使用deps就像调用require([]),但它在loader处理配置完毕之后就立即生效。它并不阻塞其他的require()调用,它仅是指定某些模块作为config块的一部分而异步加载的手段而已。

 

AMD 规范

异步模块定义(AMD)的编程接口提供了定义模块,及异步加载该模块的依赖的机制。它非常适合于使用于浏览器环境,浏览器的同步加载模块机制会带来性能,可用性,调试和跨域访问的问题。

API 说明

define() 函数

本规范只定义了一个函数 "define",它是全局变量。函数的描述为:

    define(id?, dependencies?, factory);

 

名字

第一个参数,id,是个字符串,它指的是定义中模块的名字,这个参数是可选的。如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字。如果提供了该参数,模块名必须“顶级”的和绝对的(不允许相对名字)。

模块名的格式

模块名用来唯一标识定义中模块,它们同样在依赖性数组中使用。AMD的模块名规范是是CommonJS模块名规范的超集。引用如下:

· 模块名是用正斜杠分割的有意义单词的字符串

· 单词须为驼峰形式,或者"."".."

· 模块名不允许文件扩展名的形式,如“.js

· 模块名可以为 "相对的或 "顶级的"。如果首字符为“.”或“..”则为相对的模块名

· 顶级的模块名从根命名空间的概念模块解析

· 相对的模块名从 "require" 书写和调用的模块解析

CommonJS模块id属性常被用来JavaScript模块。

依赖

第二个参数,dependencies,是个定义中模块所依赖模块的数组。依赖模块必须根据模块的工厂方法优先级执行,并且执行的结果应该按照依赖数组中的位置顺序以参数的形式传入定义中模块的)工厂方法中。

依赖的模块名如果是相对的,应该解析为相对定义中模块。换句话来说,相对名解析为相对模块的名字,并非相对于寻找该模块的名字的路径。

本规范定义了截然不同的三种特殊的依赖关键字。如果"require","exports", 或 "module"出现在依赖列表中,参数应该按照CommonJS模块规范自由变量去解析。

依赖参数是可选的,如果忽略此参数,它应该默认为["require", "exports", "module"]。然而,如果工厂方法的长度属性小于3,加载器会选择以函数指定的参数个数 调用工厂方法

工厂方法

第三个参数,factory,为模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次如果是对象,此对象应该为模块的输出值

如果工厂方法返回一个值(对象,函数,或任意强制类型转换为true的值),应该为设置为模块的输出值。

简单的 CommonJS 转换

如果依赖性参数被忽略,模块加载器可以选择扫描工厂方法中的require语句以获得依赖性(字面量形为require("module-id"))。工厂函数第一个参数必须为require从而使此机制正常工作。

在某些情况下,因为脚本大小的限制或函数不支持toString方法(Opera Mobile是已知的不支持函数的toString方法),模块加载器可以选择不扫描依赖性。

如果有依赖参数,模块加载器不应该在工厂方法中扫描依赖性。

define.amd 属性

为了清晰的标识标识全局函数(为浏览器加载script必须的)遵从AMD编程接口。任何全局函数应该有一个"amd"的属性,它的值为一个对象。这样可以防止与现有的定义了define函数但不遵从AMD编程接口的代码相冲突。

当前,define.amd对象的属性没有包含在本规范中。实现本规范的作者,可以用它通知超出本规范编程接口基本实现的额外能力。

define.amd的存在表明函数遵循本规范。如果有另外一个版本的编程接口,那么应该定义另外一个属性,如define.amd2,表明实现只遵循该版本的编程接口。

一个如何定义同一个环境中允许多次加载同一个版本的模块的实现:

    define.amd = {

      multiversion: true

    };

 

The minimum definition:

    define.amd = {};

 

一次输出多个模块

在一个脚本中可以使用多次define调用。这些define调用的顺序不应该是重要的。早一些的模块定义中所指定的依赖,可以在同一脚本中晚一些定义。这被模块加载器用来延迟加载未解决的依赖,直到全部脚本加载完毕,防止没必要的请求。

例子

使用 require 和 exports

创建一个名为"alpha"的模块,使用了requireexports,和名为"beta"的模块:

   define("alpha", ["require""exports""beta"], function (require, exports, beta) {

       exports.verb = function() {

           return beta.verb();

           //Or:

           return require("beta").verb();

       }

   });

 

一个返回对象的匿名模块:

   define(["alpha"], function (alpha) {

       return {

         verb: function(){

           return alpha.verb() + 2;

         }

       };

   });

 

一个没有依赖性的模块可以直接定义对象:

   define({

     add: function(x, y){

       return x + y;

     }

   });

 

一个使用了简单CommonJS转换的模块定义:

   define(function (require, exports, module) {

     var a = require('a'),

         b = require('b');

 

     exports.action = function () {};

   });

 

全局变量

本规范保留全局变量"define"以用来实现本规范。包额外信息异步定义编程接口是为将来的CommonJS API保留的。模块加载器不应在此函数添加额外的方法或属性。

本规范保留全局变量"require"被模块加载器使用。模块加载器可以在合适的情况下自由地使用该全局变量。它可以使用这个变量或添加任何属性以完成模块加载器的特定功能。它同样也可以选择完全不使用"require"

CommonJS的关系

AMD可以作为一个中转的版本作为CommonJS模块只要CommonJS没有被用来同步的require调用。使用同步require调用的CommonJS代码可以被转换为使用回调风格的AMD模块加载器。

 

0 0
原创粉丝点击