什么是模塊化開發?
前端開發中,起初只要在script標簽中嵌入幾十上百行代碼就能實現一些基本的交互效果,后來js得到重視,應用也廣泛起來了,jQuery,Ajax,Node.Js,MVC,MVVM等的助力也使得前端開發得到重視,也使得前端項目越來越復雜,然而,JavaScript卻沒有為組織代碼提供任何明顯幫助,甚至沒有類的概念,更不用說模塊(module)了,那么什么是模塊呢?
一個模塊就是實現特定功能的文件,有了模塊,我們就可以更方便地使用別人的代碼,想要什么功能,就加載什么模塊。模塊開發需要遵循一定的規范,否則就都亂套了。
根據AMD規范,我們可以使用define定義模塊,使用require調用模塊。
目前,通行的js模塊規范主要有兩種:CommonJS和AMD。
AMD規范
AMD 即Asynchronous Module Definition,中文名是“異步模塊定義”的意思。它是一個在瀏覽器端模塊化開發的規范,服務器端的規范是CommonJS
模塊將被異步加載,模塊加載不影響后面語句的運行。所有依賴某些模塊的語句均放置在回調函數中。
AMD是RequireJS在推廣過程中對模塊定義的規范化的產出。
define() 函數
AMD規范只定義了一個函數define,它是全局變量。函數的描述為:
define(id?, dependencies?, factory);
參數說明:
id:指定義中模塊的名字,可選;如果沒有提供該參數,模塊的名字應該默認為模塊加載器請求的指定腳本的名字。如果提供了該參數,模塊名必須是“頂級”的和絕對的(不允許相對名字)。
依賴dependencies:是一個當前模塊依賴的,已被模塊定義的模塊標識的數組字面量。
依賴參數是可選的,如果忽略此參數,它應該默認為["require","exports","module"]。然而,如果工廠方法的長度屬性小于3,加載器會選擇以函數的長度屬性指定的參數個數調用工廠方法。
工廠方法factory,模塊初始化要執行的函數或對象。如果為函數,它應該只被執行一次。如果是對象,此對象應該為模塊的輸出值。
模塊名的格式
模塊名用來唯一標識定義中模塊,它們同樣在依賴性數組中使用:
模塊名是用正斜杠分割的有意義單詞的字符串
單詞須為駝峰形式,或者".",".."模塊名不允許文件擴展名的形式,如“.js”
模塊名可以為"相對的"或"頂級的"。如果首字符為“.”或“..”則為相對的模塊名
頂級的模塊名從根命名空間的概念模塊解析
相對的模塊名從"require"書寫和調用的模塊解析
使用 require 和 exports
創建一個名為"alpha"的模塊,使用了require,exports,和名為"beta"的模塊:
define("alpha", ["require","exports","beta"],function(require, exports, beta) {
exports.verb =function() {returnbeta.verb();//Or:returnrequire("beta").verb();
}
});
require API介紹:https://github.com/amdjs/amdj...
AMD規范中文版:https://github.com/amdjs/amdj...
目前,實現AMD的庫有RequireJS 、curl 、Dojo 、Nodules等。
CommonJS規范
CommonJS是服務器端模塊的規范,Node.js采用了這個規范。Node.JS首先采用了js模塊化的概念。
根據CommonJS規范,一個單獨的文件就是一個模塊。每一個模塊都是一個單獨的作用域,也就是說,在該模塊內部定義的變量,無法被其他模塊讀取,除非定義為global對象的屬性。
輸出模塊變量的最好方法是使用module.exports對象。
var i =1;
varmax=30;module.exports=function() {for(i -=1; i++
console.log(i);
}max*=1.1;
};
上面代碼通過module.exports對象,定義了一個函數,該函數就是模塊外部與內部通信的橋梁。
加載模塊使用require方法,該方法讀取一個文件并執行,最后返回文件內部的module.exports對象。
CommonJS規范:http://javascript.ruanyifeng....
RequireJS和SeaJS
RequireJS由James Burke創建,他也是AMD規范的創始人。
define方法用于定義模塊,RequireJS要求每個模塊放在一個單獨的文件里。
RequireJS和Sea.js都是模塊加載器,倡導模塊化開發理念,核心價值是讓JavaScript的模塊化開發變得簡單自然。
SeaJS與RequireJS最大的區別:
SeaJS對模塊的態度是懶執行, 而RequireJS對模塊的態度是預執行
不明白?看這篇圖文并茂的文章吧:http://www.douban.com/note/28...
RequireJS API:http://www.requirejs.cn/docs/...
RequireJS的用法:http://www.ruanyifeng.com/blo...
為什么要用requireJS
試想一下,如果一個網頁有很多的js文件,那么瀏覽器在下載該頁面的時候會先加載js文件,從而停止了網頁的渲染,如果文件越多,瀏覽器可能失去響應。其次,要保證js文件的依賴性,依賴性最大的模塊(文件)要放在最后加載,當依賴關系很復雜的時候,代碼的編寫和維護都會變得困難。
RequireJS就是為了解決這兩個問題而誕生的:
(1)實現js文件的異步加載,避免網頁失去響應;
(2)管理模塊之間的依賴性,便于代碼的編寫和維護。
RequireJS文件下載:http://www.requirejs.cn/docs/...
AMD和CMD
CMD(Common Module Definition) 通用模塊定義。該規范明確了模塊的基本書寫格式和基本交互規則。該規范是在國內發展出來的。AMD是依賴關系前置,CMD是按需加載。
在 CMD 規范中,一個模塊就是一個文件。代碼的書寫格式如下:
define(factory);
factory為函數時,表示是模塊的構造方法。執行該構造方法,可以得到模塊向外提供的接口。factory 方法在執行時,默認會傳入三個參數:require、exports 和 module:
define(function(require, exports,module){
// 模塊代碼
});
require是可以把其他模塊導入進來的一個參數,而export是可以把模塊內的一些屬性和方法導出的。
CMD規范地址:https://github.com/seajs/seaj...
AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出。CMD是 SeaJS 在推廣過程中對模塊定義的規范化產出。
對于依賴的模塊,AMD是提前執行,CMD是延遲執行。
AMD:提前執行(異步加載:依賴先執行)+延遲執行CMD:延遲執行(運行到需加載,根據順序執行)
CMD 推崇依賴就近,AMD 推崇依賴前置。看如下代碼:
// CMDdefine(function(require, exports, module) {vara= require('./a')a.doSomething()// 此處略去 100 行varb= require('./b')// 依賴可以就近書寫b.doSomething()// ...})// AMD 默認推薦的是define(['./a','./b'], function(a, b) {// 依賴必須一開始就寫好a.doSomething()// 此處略去 100 行b.doSomething()
...
})
另外一個區別是:
AMD:API根據使用范圍有區別,但使用同一個api接口CMD:每個API的職責單一
AMD的優點是:異步并行加載,在AMD的規范下,同時異步加載是不會產生錯誤的。
CMD的機制則不同,這種加載方式會產生錯誤,如果能規范化模塊內容形式,也可以
jquery1.7以上版本會自動模塊化,支持AMD模式:主要是使用define函數,sea.js雖然是CommonJS規范,但卻使用了define來定義模塊
所以jQuery已經自動模塊化了
seajs.config({'base':'/','alias':{'jquery':'jquery.js'//定義jQuery文件}
});
define函數和AMD的define類似:
define(function(require, exports,module{
//先要載入jQuery的模塊var$ =require('jquery');//然后將jQuery對象傳給插件模塊require('./cookie')($);//開始使用 $.cookie方法});
sea.js如何使用?
- 引入sea.js的庫
- 如何變成模塊?
- define
-3.如何調用模塊?
-exports
-sea.js.use
-4.如何依賴模塊?
-require
define(function (require,exports,module) {//exports : 對外的接口//requires : 依賴的接口require('./test.js');//如果地址是一個模塊的話,那么require的返回值就是模塊中的exports
})
sea.js 開發實例
鼠標拖拽的模塊化開發實踐#div1{width:200px;height:200px;background:black;position:absolute;display:none;}#div2{width:30px;height:30px;background:yellow;position:absolute;bottom:0;right:0;}#div3{width:100px;height:100px;background:blue;position:absolute;right:0;top:0;}//A同事 :seajs.use('./main.js');
A同事
//A同事寫的main.js:define(function(require,exports,module) {varoInput =document.getElementById('input1');varoDiv1 =document.getElementById('div1');varoDiv2 =document.getElementById('div2');varoDiv3 =document.getElementById('div3');require('./drag.js').drag(oDiv3);
oInput.onclick =function() {
oDiv1.style.display ='block';require('./scale.js').scale(oDiv1,oDiv2);require.async('./scale.js',function(ex) {
ex.scale(oDiv1,oDiv2);
})
}
});
B同事
//B同事寫的drag.js:define(function(require,exports,module){functiondrag(obj){vardisX =0;vardisY =0;
obj.onmousedown =function(ev){varev = ev ||window.event;
disX = ev.clientX - obj.offsetLeft;
disY = ev.clientY - obj.offsetTop;document.onmousemove =function(ev){varev = ev ||window.event;varL =require('./range.js').range(ev.clientX - disX ,document.documentElement.clientWidth - obj.offsetWidth ,0);varT =require('./range.js').range(ev.clientY - disY ,document.documentElement.clientHeight - obj.offsetHeight ,0);
obj.style.left = L +'px';
obj.style.top = T +'px';
};document.onmouseup =function(){document.onmousemove =null;document.onmouseup =null;
};returnfalse;
};
}
exports.drag = drag;//對外提供接口});
C同事
//C同事寫的scale.js:define(function(require,exports,module){functionscale(obj1,obj2){vardisX =0;vardisY =0;vardisW =0;vardisH =0;
obj2.onmousedown =function(ev){varev = ev ||window.event;
disX = ev.clientX;
disY = ev.clientY;
disW = obj1.offsetWidth;
disH = obj1.offsetHeight;document.onmousemove =function(ev){varev = ev ||window.event;varW =require('./range.js').range(ev.clientX - disX + disW ,500,100);varH =require('./range.js').range(ev.clientY - disY + disH ,500,100);
obj1.style.width = W +'px';
obj1.style.height = H +'px';
};document.onmouseup =function(){document.onmousemove =null;document.onmouseup =null;
};returnfalse;
};
}
exports.scale = scale;
});
D同事
// D同事的range.js--限定拖拽范圍
define(function(require,exports,module){functionrange(iNum,iMax,iMin){if( iNum > iMax ){returniMax;
}elseif( iNum < iMin ){returniMin;
}else{returniNum;
}
}
exports.range=range;
});
requirejs開發實例
require.config是用來定義別名的,在paths屬性下配置別名。然后通過requirejs(參數一,參數二);參數一是數組,傳入我們需要引用的模塊名,第二個參數是個回調函數,回調函數傳入一個變量,代替剛才所引入的模塊。
main.js文件
//別名配置requirejs.config({
paths: {
jquery:'jquery.min'//可以省略.js}
});//引入模塊,用變量$表示jquery模塊requirejs(['jquery'],function($) {
$('body').css('background-color','red');
});
引入模塊也可以只寫require()。requirejs通過define()定義模塊,定義的參數上同。在此模塊內的方法和變量外部是無法訪問的,只有通過return返回才行.
define 模塊
define(['jquery'],function($) {//引入jQuery模塊return{
add:function(x,y){returnx + y;
}
};
});
將該模塊命名為math.js保存。
main.js引入模塊方法
require(['jquery','math'],function($,math) {console.log(math.add(10,100));//110});
沒有依賴
如果定義的模塊不依賴其他模塊,則可以:
define(function() {return{name:"trigkit4",
age:"21"}
});
AMD推薦的風格通過返回一個對象做為模塊對象,CommonJS的風格通過對module.exports或exports的屬性賦值來達到暴露模塊對象的目的