RequireJS和AMD規範

RequireJS和AMD規範javascript

轉載來自:《JavaScript 標準參考教程(alpha)》html

概述

RequireJS是一個工具庫,主要用於客戶端的模塊管理。它可讓客戶端的代碼分紅一個個模塊,實現異步或動態加載,從而提升代碼的性能和可維護性。它的模塊管理遵照AMD規範(Asynchronous Module Definition)。java

RequireJS的基本思想是,經過define方法,將代碼定義爲模塊;經過require方法,實現代碼的模塊加載。node

首先,將require.js嵌入網頁,而後就能在網頁中進行模塊化編程了。jquery

<script data-main="scripts/main" src="scripts/require.js"></script>

上面代碼的data-main屬性不可省略,用於指定主代碼所在的腳本文件,在上例中爲scripts子目錄下的main.js文件。用戶自定義的代碼就放在這個main.js文件中。git

define方法:定義模塊

define方法用於定義模塊,RequireJS要求每一個模塊放在一個單獨的文件裏。github

按照是否依賴其餘模塊,能夠分紅兩種狀況討論。第一種狀況是定義獨立模塊,即所定義的模塊不依賴其餘模塊;第二種狀況是定義非獨立模塊,即所定義的模塊依賴於其餘模塊。ajax

(1)獨立模塊正則表達式

若是被定義的模塊是一個獨立模塊,不須要依賴任何其餘模塊,能夠直接用define方法生成。npm

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

上面代碼生成了一個擁有method一、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。

(完)

相關文章
相關標籤/搜索