AMD、CMD和Common规范
1.名詞解釋
AMD:Asynchronous Modules Definition異步模塊定義,提供定義模塊及異步加載該模塊依賴的機制。
CMD:Common Module Definition 通用模塊定義,提供模塊定義及按需執行模塊
RequireJS 遵循 AMD(異步模塊定義)規范,Sea.js 遵循 CMD (通用模塊定義)規范。規范的不同,導致了兩者 API 不同。
2. 提前執行:提前異步并行加載
優點:盡早執行依賴可以盡早發現錯誤;缺點:容易產生浪費
3. 延遲執行:延遲按需加載
優點:減少資源浪費 缺點:等待時間長、出錯時間延后
2.1 AMD與CMD代碼模式
AMD代碼模式-運行策略
define(['./a', './b'], function(a, b) { //運行至此,a.js和b.js已經下載完成 a模塊和b模塊已經執行完,直接可用;
a.doing();
// 此處省略500行代碼
b.doing();
});
CMD代碼模式-運行策略
define(function(require, exports, module) {
var a = require("./a"); //等待a.js下載、執行完
a.doing();
// 此處省略500行代碼
var b = require("./b"); //依賴就近書寫
b.doing();
});
3. AMD 的 API 默認是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,沒有全局 require,而是根據模塊系統的完備性,提供 seajs.use 來實現模塊系統的加載啟動。CMD 里,每個 API 都簡單純粹。
方案 | 優勢 | 劣勢 | 特點
AMD | 速度快 | 會浪費資源 | 預先加載所有的依賴,直到使用的時候才執行
CMD | 只有真正需要才加載依賴 | 性能較差 | 直到使用的時候才定義依賴
它們除了希望放在瀏覽器作為loader也能夠放在服務端,提供加載功能。在我看來,AMD擅長在瀏覽器端、CMD擅長在服務器端。這是因為瀏覽器加載一個功能不像服務器那么快,有大量的網絡消耗。所以一個異步loader是更接地氣的。
或者,干脆使用YUI3的模塊機制,在上線前進行壓制。把互相依賴的模塊壓在一個文件中。
---------------------------------------------------------------------------------------------------
每一個卓越的思想都有一份樸實的代碼實現。所以無論AMD與CMD都要面臨以下幾個問題:
scriptElement= document.createElement('script');
scriptElement.src = moduleUrl;
scriptElement.async = true;
scriptElement.onload = function(){.........};
document.head.appendChild(scriptElement);
5、模塊加載完畢后,獲取依賴項(amd、cmd區別),改變模塊status,由statuschange后,檢測所有模塊的依賴項。
由于requirejs與seajs遵循規范不同,requirejs在define函數中可以很容易獲得當前模塊依賴項。而seajs中不需要依賴聲明,所以必須做一些特殊處理才能否獲得依賴項。方法將factory作toString處理,然后用正則匹配出其中的依賴項,比如出現require(./a),則檢測到需要依賴a模塊。
同時滿足非阻塞和順序執行就需要需要對代碼進行一些預處理,這是由于CMD規范和瀏覽器環境特點所決定的。
6、如果模塊的依賴項完全加載完畢(amd中需要執行完畢,cmd中只需要文件加載完畢,注意這時候的factory尚未執行,當使用require請求該模塊時,factory才會執行,所以在性能上seajs遜于requirejs),執行主模塊的factory函數;否則進入步驟3.
AMD規范定義了一個自由變量或者說是全局變量 define 的函數
define( id?, dependencies?, factory );
- define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
- export.verb = function(){
- return beta.verb();
- // or:
- return require("beta").verb();
- }
- });
- define(["alpha"], function( alpha ){
- return {
- verb : function(){
- return alpha.verb() + 1 ;
- }
- }
- });
- define( {
- add : function( x, y ){
- return x + y ;
- }
- } );
局部 與 全局 的require
- define( ['require'], function( require ){
- // ...
- } );
- or:
- define( function( require, exports, module ){
- // ...
- } );
- require(String)
- define( function( require ){
- var a = require('a'); // 加載模塊a
- } );
- require(Array, Function)
- define( function( require ){
- require( ['a', 'b'], function( a,b ){ // 加載模塊a b 使用
- // 依賴 a b 模塊的運行代碼
- } );
- } );
- require.toUrl( Url )
- define( function( require ){
- var temp = require.toUrl('./temp/a.html'); // 加載頁面
- } );
- define({ "foo": "bar" });
- define('this is {{data}}.');
- define( function(require, exports, module) {
- // 模塊代碼
- });
- define( 'module', ['module1', 'module2'], function( require, exports, module ){
- // 模塊代碼
- } );
- define(function( require, exports ){
- var a = require('./a');
- a.doSomething();
- });
- define( function(require, exports, module) {
- require.async('.a', function(a){
- a.doSomething();
- });
- });
- define(function( require, exports ){
- exports.foo = 'bar'; // 向外提供的屬性
- exports.do = function(){}; // 向外提供的方法
- });
- define(function( require, exports ){
- return{
- foo : 'bar', // 向外提供的屬性
- do : function(){} // 向外提供的方法
- }
- });
- define({
- foo : 'bar', // 向外提供的屬性
- do : function(){} // 向外提供的方法
- });
- define(function( require, exports ){
- exports = {
- foo : 'bar', // 向外提供的屬性
- do : function(){} // 向外提供的方法
- }
- });
- define(function( require, exports, module ){
- module.exports = {
- foo : 'bar', // 向外提供的屬性
- do : function(){} // 向外提供的方法
- }
- });
這種方式通過一個叫做require的方法,同步加載依賴,然后返導出API供其它模塊使用,一個模塊可以通過exports或者module.exports導出API。CommonJS規范中,一個單獨的文件就是一個模塊。每一個模塊都是一個單獨的作用域,在一個文件中定義的變量,都是私有的,對其他文件是不可見的。
Well
服務端模塊可以很好的復用
這種風格的模塊已經很多了,比如npm上基本上都是這種風格的module
簡單易用
Less Well
加載模塊是同步的,所以只有加載完成才能執行后面的操作
多個模塊不能并行加載
像Node.js主要用于服務器的編程,加載的模塊文件一般都已經存在本地硬盤,所以加載起來比較快,不用考慮異步加載的方式,所以CommonJS規范比較適用。但如果是瀏覽器環境,要從服務器加載模塊,這是就必須采用異步模式。所以就有了 AMD 、CMD 的解決方案。
CommonJS規范
- // moduleA.js
- module.exports = function( value ){
- return value * 2;
- }
- // moduleB.js
- var multiplyBy2 = require('./moduleA');
- var result = multiplyBy2(4);
CommonJS是同步加載模塊,但其實也有瀏覽器端的實現,其原理是將所有模塊都定義好并通過id進行索引,這樣就可以瀏覽器進行解析了
服務器端的Node.js遵循CommonJS規范。核心思想是允許模塊通過require 方法來同步加載所要依賴的其他模塊,然后通過 exports或module.exports來導出需要暴露的接口。
- require("module");
- require("../file.js");
- exports.doStuff = function() {};
- module.exports = someValue;
- 服務器端便于重用
- NPM中已經將近20w個模塊包
- 簡單并容易使用
- 同步的模塊方式不適合不適合在瀏覽器環境中,同步意味著阻塞加載,瀏覽器資源是異步加載的
- 不能非阻塞的并行加載多個模塊
AMD
- define("module", ["dep1", "dep2"], function(d1, d2) {
- return someExportedValue;
- });
- require(["module", "../file"], function(module, file) { /* ... */ });
優點:
- 適合在瀏覽器環境異步加載
- 并行加載多個模塊
- 提高開發成本,代碼閱讀和書寫比較困難
- 不符合通用的模塊思維方式,是一種妥協的實現
CMD
CMD規范和AMD相似,盡量保持簡單,并且與CommonJS和NodeJS的Modules規范保持了很大的兼容性。
- define(function(require, exports, module) {
- var $ = require('jquery');
- var Spinning = require('./spinning');
- exports.doSomething = ...
- module.exports = ...
- })
優點:
- 依賴就近,延遲執行
- 很容易在node中運行
- 依賴SPM打包,模塊的加載邏輯偏重
總結
以上是生活随笔為你收集整理的AMD、CMD和Common规范的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转载】Android数据存储之SQLi
- 下一篇: SpringBoot + SpringC