我們?yōu)槭裁匆K化逃顶?
一般出來什么東西讨便,說明當(dāng)前的工具有其缺陷,或者是為了方便什么以政。
那么我們就來看看通過模塊霸褒,我們可以做到什么?
我們可以只關(guān)注自己的核心邏輯盈蛮,而不用考慮其中的輔助功能废菱。比如我們想要實現(xiàn)計算兩個文本框中數(shù)值的和,我們只需要關(guān)注業(yè)務(wù)邏輯,而不需要關(guān)注自己如何查找到該元素(查找到元素可以交給jquery來做,還解決了兼容性問題)殊轴。
通過模塊衰倦,我們可以方便的使用別人的代碼,需要什么模塊就引入對應(yīng)的依賴模塊旁理。
我們先來看看幾種原生js的實現(xiàn)模塊化得方法(下面說的每一個方法都是為了解決上一種方法出現(xiàn)的問題):
-
原始的模塊寫法:
function a1(){ } function a2(){ } 缺點(diǎn):污染全局變量樊零,可能造成變量沖突,無法直觀的顯示二者的聯(lián)系
解決上一問題
var module = {
a1: function(){
},
a2: function(){
}
}
缺點(diǎn):用戶可以在外面修改module內(nèi)的變量
- 解決上一問題
var module = (function(){
var m1 = function(){
};
var m2 = function(){
};
return {
m1 : m1,
m2 : m2
};
});
通過立即執(zhí)行函數(shù)孽文,這樣就解決了模塊內(nèi)部變量可能被修改的問題
缺點(diǎn): 如果此模塊需要依賴另一個模塊才能生效
- 解決上一問題
var module = (function(mod){
mod.m1 = function(){
};
return mod;
})(another_module);
下面我們介紹現(xiàn)在應(yīng)用廣泛的javascript模塊規(guī)范:CommonJS,AMD,以及國內(nèi)的CMD驻襟。
- CommonJS
我們先來說CommonJS,在其中有一個require()方法芋哭,用于加載模塊
比如:
假設(shè)我們的一個模塊(add_module)中有一個求兩數(shù)之和的方法沉衣,在另一個地方我們就可以通過下面的方法使用這個求和方法。
var addModule = require(‘a(chǎn)dd_module’);
addModule.add(1,2);
- AMD
既然已經(jīng)有了CommonJS减牺,為什么還會出現(xiàn)AMD呢豌习?
因為CommonJS不適合瀏覽器環(huán)境,上面的代碼假如在瀏覽器中執(zhí)行拔疚,那么瀏覽器需要時間來加載add_module肥隆,如果網(wǎng)絡(luò)條件不好,瀏覽器會出現(xiàn)“假死”狀態(tài)草雕。因此瀏覽器端不能采用“同步加載”巷屿,而應(yīng)該使用“異步加載”。
AMD(異步模塊定義)墩虹,采用異步的方式加載模塊嘱巾,所有需要依賴其他模塊的語句都被定義在回調(diào)函數(shù)中。
用AMD的語法改寫上面CommonJS的寫法:
require([‘a(chǎn)dd_module’],function(addModule){
addModule.add(1,2);
});
- 基于AMD規(guī)范的javascript庫:require.js
require.js是為了實現(xiàn)下面的兩個效果:
- 實現(xiàn)js文件的異步加載诫钓,避免網(wǎng)頁失去響應(yīng)旬昭;
- 管理模塊之間的依賴性,便于代碼的編寫和維護(hù)菌湃。
3.1 使用require.js
從官網(wǎng)下載require.js问拘,在html中引入
<script src="js/require.js" defer async="true" ></script>
async屬性表明這個文件需要異步加載,避免網(wǎng)頁失去響應(yīng)惧所。IE不支持這個屬性骤坐,只支持defer,所以把defer也寫上下愈。
引入我們自己寫的需要執(zhí)行的js文件
<script src="js/require.js" data-main=“main” defer async="true" ></script>
data-main屬性的作用是纽绍,指定網(wǎng)頁程序的主模塊。在上例中势似,就是js目錄下面的main.js拌夏,這個文件會第一個被require.js加載僧著。由于require.js默認(rèn)的文件后綴名是js,所以可以把main.js簡寫成main障簿。
main.js文件:
require(['moduleA', 'moduleB'], function (moduleA, moduleB){
// some code here
});
require()函數(shù)接受兩個參數(shù)盹愚。第一個參數(shù)是一個數(shù)組,表示所依賴的模塊站故,上例就是['moduleA', 'moduleB']皆怕,即主模塊依賴這2個模塊宙帝;第二個參數(shù)是一個回調(diào)函數(shù)纸肉,當(dāng)前面指定的模塊都加載成功后,它將被調(diào)用述寡。加載的模塊會以參數(shù)形式傳入該函數(shù)污淋,從而在回調(diào)函數(shù)內(nèi)部就可以使用這些模塊。
require()異步加載moduleA余掖,moduleB寸爆,瀏覽器不會失去響應(yīng);它指定的回調(diào)函數(shù)盐欺,只有前面的模塊都加載成功后赁豆,才會運(yùn)行,解決了依賴性的問題冗美。
比如我們需要依賴jquery
require(['jquery'], function ($){
// some code here
});
主模塊的依賴模塊是['jquery']魔种。
默認(rèn)情況下,require.js假定這個模塊與main.js在同一個目錄粉洼,文件名為jquery.js节预,然后自動加載。
使用require.config()方法属韧,我們可以對模塊的加載行為進(jìn)行自定義安拟。require.config()就寫在主模塊(main.js)的頭部。
參數(shù)就是一個對象宵喂,這個對象的paths屬性指定各個模塊的加載路徑糠赦。
如果我們的依賴文件和main.js不在同一文件夾下呢?
require.config({
paths: {
"jquery": "lib/jquery.min"
}
});
也可以通過配置baseUrl來改變基目錄
require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min"
}
});
自定義的require.js加載的模塊锅棕,采用AMD規(guī)范拙泽,即就是模塊必須采用特定的define()函數(shù)來定義。
- 如果一個模塊不依賴其他模塊裸燎,那么可以直接定義在define()函數(shù)之中顾瞻。
define(function (){
var add = function (x,y){
return x+y;
};
return {
add: add
};
});
- 如果這個模塊還依賴其他模塊,那么define()函數(shù)的第一個參數(shù)顺少,必須是一個數(shù)組朋其,指明該模塊的依賴性王浴。
define(['myLib'], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
});
加載非規(guī)范的模塊
理論上,require.js加載的模塊梅猿,必須是按照AMD規(guī)范氓辣、用define()函數(shù)定義的模塊。但是實際上袱蚓,雖然已經(jīng)有一部分流行的函數(shù)庫(比如jQuery)符合AMD規(guī)范钞啸,更多的庫并不符合。那么喇潘,require.js是否能夠加載非規(guī)范的模塊呢体斩?
回答是可以的。
這樣的模塊在用require()加載之前颖低,要先用require.config()方法絮吵,定義它們的一些特征。
舉例來說忱屑,underscore和backbone這兩個庫蹬敲,都沒有采用AMD規(guī)范編寫。如果要加載它們的話莺戒,必須先定義它們的特征伴嗡。
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
require.config()接受一個配置對象,這個對象除了有前面說過的paths屬性之外从铲,還有一個shim屬性瘪校,專門用來配置不兼容的模塊。具體來說名段,每個模塊要定義(1)exports值(輸出的變量名)阱扬,表明這個模塊外部調(diào)用時的名稱;(2)deps數(shù)組吉嫩,表明該模塊的依賴性价认。
4.CMD
關(guān)于CMD的介紹,大家可以點(diǎn)擊這里查看自娩;
關(guān)于AMD和CMD的比較用踩,大家可以點(diǎn)擊這里看玉伯寫的一篇評論;
SeaJS和RequireJS的區(qū)別忙迁,大家可以點(diǎn)擊這里脐彩。
本文內(nèi)容是我自己在阮一峰博客的基礎(chǔ)上整理的,原文大家可以查看:
http://www.ruanyifeng.com/blog/2012/10/javascript_module.html
http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_definition.html
http://www.ruanyifeng.com/blog/2012/11/require_js.html