編寫CommonJS, AMD, CMD 和 原生 JS都可以使用的插件

最近幾年盟步,我們可以選擇的Javascript組件的生態(tài)系統(tǒng)一直在穩(wěn)步增長。雖然陡增的選擇范圍是極好的榜苫,但當(dāng)組件混合匹配使用時就會出現(xiàn)很尷尬的局面铝宵。開發(fā)新手們會很快發(fā)現(xiàn)不是所有組件都能彼此“和平相處”。

為了解決這個問題看成,兩種競爭關(guān)系的模塊規(guī)范AMD和CommonJS問世了君编,它們允許開發(fā)者遵照一種約定的沙箱化和模塊化的方式來寫代碼,這樣就能避免“污染生態(tài)系統(tǒng)”川慌。

Javascript模塊化

在了解這些規(guī)范之前吃嘿,還是先了解一下什么是模塊化。

模塊化是指在解決某一個復(fù)雜問題或者一系列的雜糅問題時梦重,依照一種分類的思維把問題進行系統(tǒng)性的分解以之處理兑燥。模塊化是一種處理復(fù)雜系統(tǒng)分解為代碼結(jié)構(gòu)更合理,可維護性更高的可管理的模塊的方式忍饰√吧可以想象一個巨大的系統(tǒng)代碼,被整合優(yōu)化分割成邏輯性很強的模塊時艾蓝,對于軟件是一種何等意義的存在力崇。對于軟件行業(yè)來說:解耦軟件系統(tǒng)的復(fù)雜性,使得不管多么大的系統(tǒng)赢织,也可以將管理亮靴,開發(fā),維護變得“有理可循”于置。

還有一些對于模塊化一些專業(yè)的定義為:模塊化是軟件系統(tǒng)的屬性茧吊,這個系統(tǒng)被分解為一組高內(nèi)聚,低耦合的模塊。那么在理想狀態(tài)下我們只需要完成自己部分的核心業(yè)務(wù)邏輯代碼搓侄,其他方面的依賴可以通過直接加載被人已經(jīng)寫好模塊進行使用即可瞄桨。

首先,既然是模塊化設(shè)計讶踪,那么作為一個模塊化系統(tǒng)所必須的能力:

  • 定義封裝的模塊芯侥。
  • 定義新模塊對其他模塊的依賴。
  • 可對其他模塊的引入支持乳讥。
    好了柱查,思想有了,那么總要有點什么來建立一個模塊化的規(guī)范制度吧云石,不然各式各樣的模塊加載方式只會將局?jǐn)嚨酶鼮榛靵y唉工。那么在JavaScript中出現(xiàn)了一些非傳統(tǒng)模塊開發(fā)方式的規(guī)范 CommonJS的模塊規(guī)范,AMD(Asynchronous Module Definition)汹忠,CMD(Common Module Definition)等淋硝。

CommonJS

CommonJS是服務(wù)器端模塊的規(guī)范,Node.js采用了這個規(guī)范错维。

根據(jù)CommonJS規(guī)范奖地,一個單獨的文件就是一個模塊。加載模塊使用require方法赋焕,該方法讀取一個文件并執(zhí)行参歹,最后返回文件內(nèi)部的exports對象。

CommonJS 有三個全局變量 module隆判、exports 和 require犬庇。但是由于 AMD 也有 require 這個全局變量,故不使用這個require變量來進行檢測侨嘀。

如果想要對外提供接口的話臭挽,可以將接口綁定到 exports (即 module.exports) 上。

function MyModule() {
    // ...
}

if(typeof module !== `undefined` && typeof exports === `object`) {
    module.exports = MyModule;
}

CommonJS 加載模塊是同步的咬腕,所以只有加載完成才能執(zhí)行后面的操作欢峰。像Node.js主要用于服務(wù)器的編程,加載的模塊文件一般都已經(jīng)存在本地硬盤涨共,所以加載起來比較快纽帖,不用考慮異步加載的方式,所以CommonJS規(guī)范比較適用举反。但如果是瀏覽器環(huán)境懊直,要從服務(wù)器加載模塊,這是就必須采用異步模式火鼻。所以就有了 AMD CMD 解決方案室囊。

AMD

AMD是"Asynchronous Module Definition"的縮寫雕崩,意思就是"異步模塊定義".

由于不是JavaScript原生支持,使用AMD規(guī)范進行頁面開發(fā)需要用到對應(yīng)的庫函數(shù)融撞,也就是大名鼎鼎RequireJS盼铁,實際上AMD 是 RequireJS 在推廣過程中對模塊定義的規(guī)范化的產(chǎn)出

AMD設(shè)計出一個簡潔的寫模塊API:
模塊定義define(id?, dependencies?, factory);
第一個參數(shù) id 為字符串類型,表示了模塊標(biāo)識懦铺,為可選參數(shù)捉貌。若不存在則模塊標(biāo)識應(yīng)該默認定義為在加載器中被請求腳本的標(biāo)識支鸡。如果存在冬念,那么模塊標(biāo)識必須為頂層的或者一個絕對的標(biāo)識。
第二個參數(shù)牧挣,dependencies 急前,是一個當(dāng)前模塊依賴的,已被模塊定義的模塊標(biāo)識的數(shù)組字面量瀑构。
第三個參數(shù)裆针,factory,是一個需要進行實例化的函數(shù)或者一個對象寺晌。

通過參數(shù)的排列組合世吨,這個簡單的API可以從容應(yīng)對各種各樣的應(yīng)用場景,如下所述呻征。

//3個參數(shù)耘婚,定義一個名為 ‘a(chǎn)lpha’ 的模塊,依賴于require陆赋,exports沐祷,beta這三個模塊
define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){
    export.verb = function(){
        return beta.verb();
        // or:
        return require("beta").verb();
    }
});

//2個參數(shù),定義一個匿名模塊攒岛,依賴于require赖临,exports,beta這三個模塊
define([ "require", "exports", "beta" ], function( require, exports, beta ){
    export.verb = function(){
        return beta.verb();
        // or:
        return require("beta").verb();
    }
});

//1個參數(shù),定義一個匿名模塊灾锯,不依賴其他模塊
define(function(){
    return 'hello world';
});

也可定義定義數(shù)據(jù)對象模塊

//1個參數(shù),定義一個定義數(shù)據(jù)對象模塊
define({
    users: [],
    members: []
});

//等價于
define(function(){
    return{
        users: [],
        members: []
    }
});

除了define外兢榨,AMD還保留一個關(guān)鍵字require。require 作為規(guī)范保留的全局標(biāo)識符顺饮,可以實現(xiàn)為 module loader吵聪,也可以不實現(xiàn)。

模塊加載require([module], callback)

require(['math'], function(math) {
 math.add(2, 3);
});

AMD 規(guī)范中领突,define 函數(shù)同樣有一個公有屬性 define.amd暖璧。

AMD 中的參數(shù)便是這個模塊的依賴。那么如何在 AMD 中提供接口呢君旦?它是返回一個對象澎办,這個對象就作為這個模塊的接口嘲碱,故我們可以這樣寫:

function MyModule() {
    // ...
}

if(typeof define === `function` && define.amd) {
    define(function() { return MyModule; });
}

CMD

CMD 即Common Module Definition通用模塊定義,CMD規(guī)范是國內(nèi)發(fā)展出來的局蚀,就像AMD有個requireJS麦锯,CMD有個瀏覽器的實現(xiàn)SeaJS,SeaJS要解決的問題和requireJS一樣琅绅,只不過在模塊定義方式和模塊加載(可以說運行扶欣、解析)時機上有所不同

語法
Sea.js 推崇一個模塊一個文件,遵循統(tǒng)一的寫法
define(id?, deps?, factory)
因為CMD推崇

  • 一個文件一個模塊千扶,所以經(jīng)常就用文件名作為模塊id
  • CMD推崇依賴就近料祠,所以一般不在define的參數(shù)中寫依賴,在factory中寫

factory是一個函數(shù)澎羞,有三個參數(shù)髓绽,function(require, exports, module)

  • require 是一個方法,接受 模塊標(biāo)識 作為唯一參數(shù)妆绞,用來獲取其他模塊提供的接口:require(id)
  • exports 是一個對象顺呕,用來向外提供模塊接口
  • module 是一個對象,上面存儲了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法

不考慮多了一層函數(shù)外括饶,格式和Node.js是一樣的:使用require獲取依賴模塊株茶,使用exports導(dǎo)出API。

// 定義模塊  myModule.js
define(function(require, exports, module) {
    var a = require('a'),
          b = require('b');

    exports.action = function() {};
} );

// 加載模塊
seajs.use(['myModule.js'], function(my){
      ...
});

有人說CMD是同步的图焰,其實CMD是異步加載的启盛,CMD加載完某個依賴模塊后并不執(zhí)行,只是下載而已楞泼,在所有依賴模塊加載完成后進入主邏輯驰徊,遇到require語句的時候才執(zhí)行對應(yīng)的模塊,這樣模塊的執(zhí)行順序和書寫順序是完全一致的

CMD 與 AMD 區(qū)別

最明顯的區(qū)別就是在模塊定義時對依賴的處理不同

  • 對于依賴的模塊AMD是提前執(zhí)行堕阔,CMD是延遲執(zhí)行棍厂。不過RequireJS從2.0開始,也改成可以延遲執(zhí)行(根據(jù)寫法不同超陆,處理方式不通過)牺弹。

  • CMD推崇依賴就近,只有在用到某個模塊的時候再去require ,AMD推崇依賴前置时呀。

//AMD
define(['./a','./b'], function (a, b) {
 
    //依賴一開始就寫好
    a.test();
    b.test();
});
 
//CMD
define(function (requie, exports, module) {
     
    //依賴可以就近書寫
    var a = require('./a');
    a.test();
     
    ...
    //軟依賴
    if (status) {
     
        var b = requie('./b');
        b.test();
    }
});

這種區(qū)別各有優(yōu)劣张漂,只是語法上的差距,而且requireJS和SeaJS都支持對方的寫法

AMD和CMD最大的區(qū)別是對依賴模塊的執(zhí)行時機處理不同谨娜,注意不是加載的時機或者方式不同

同樣都是異步加載模塊航攒,AMD在加載模塊完成后就會執(zhí)行改模塊,所有模塊都加載執(zhí)行完后會進入require的回調(diào)函數(shù)趴梢,執(zhí)行主邏輯漠畜,這樣的效果就是依賴模塊的執(zhí)行順序和書寫順序不一定一致币他,看網(wǎng)絡(luò)速度,哪個先下載下來憔狞,哪個先執(zhí)行蝴悉,但是主邏輯一定在所有依賴加載完成后才執(zhí)行

CMD加載完某個依賴模塊后并不執(zhí)行,只是下載而已瘾敢,在所有依賴模塊加載完成后進入主邏輯拍冠,遇到require語句的時候才執(zhí)行對應(yīng)的模塊,這樣模塊的執(zhí)行順序和書寫順序是完全一致的

這也是很多人說AMD用戶體驗好簇抵,因為沒有延遲庆杜,依賴模塊提前執(zhí)行了,CMD性能好正压,因為只有用戶需要的時候才執(zhí)行的原因

原生js 標(biāo)簽的方式加載

沒什么多說的欣福,其實就是掛載到 window 下

編寫通用的格式

(function (window, factory) {
    if (typeof exports === 'object') {
     
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
     
        define(factory);
    } else {
     
        window.eventUtil = factory();
    }
})(this, function () {
    // module ...
});

參考jquery,echarts可以寫的更簡單一些

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (factory((global.D3MakeSvgPicClass = {})));
}(this, (function (exports) {
    //// module ...
    exports.D3MakeSvgPicClass = MakeSvgPicClass;
 })));

依賴jquery的庫的通用寫法案例

; (function (factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof module === 'object' && module.exports) {
    // Node/CommonJS
    module.exports = function (root, jQuery) {
      if (jQuery === undefined) {
        // require('jQuery') returns a factory that requires window to
        // build a jQuery instance, we normalize how we use modules
        // that require this pattern but the window provided is a noop
        // if it's defined (how jquery works)
        if (typeof window !== 'undefined') {
          jQuery = require('jquery');
        }
        else {
          jQuery = require('jquery')(root);
        }
      }
      factory(jQuery);
      return jQuery;
    };
  } else {
    // Browser globals
    factory(jQuery);
  }
}(function (jQuery) {

    // 庫的內(nèi)容主體

 }));

參考資料:http://www.reibang.com/p/bd4585b737d7
參考資料:https://www.cnblogs.com/zzsdream/p/5158968.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末焦履,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子雏逾,更是在濱河造成了極大的恐慌嘉裤,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栖博,死亡現(xiàn)場離奇詭異屑宠,居然都是意外死亡,警方通過查閱死者的電腦和手機仇让,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門典奉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丧叽,你說我怎么就攤上這事卫玖。” “怎么了踊淳?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵假瞬,是天一觀的道長。 經(jīng)常有香客問我迂尝,道長脱茉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任垄开,我火速辦了婚禮琴许,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘溉躲。我一直安慰自己榜田,他們只是感情好寸认,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著串慰,像睡著了一般偏塞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上邦鲫,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天灸叼,我揣著相機與錄音,去河邊找鬼庆捺。 笑死古今,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的滔以。 我是一名探鬼主播捉腥,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼你画!你這毒婦竟也來了抵碟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坏匪,失蹤者是張志新(化名)和其女友劉穎拟逮,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體适滓,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡敦迄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凭迹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罚屋。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嗅绸,靈堂內(nèi)的尸體忽然破棺而出脾猛,到底是詐尸還是另有隱情,我是刑警寧澤朽砰,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布尖滚,位于F島的核電站,受9級特大地震影響瞧柔,放射性物質(zhì)發(fā)生泄漏漆弄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一造锅、第九天 我趴在偏房一處隱蔽的房頂上張望撼唾。 院中可真熱鬧,春花似錦哥蔚、人聲如沸倒谷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渤愁。三九已至牵祟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抖格,已是汗流浹背诺苹。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留雹拄,地道東北人收奔。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像滓玖,于是被迫代替她去往敵國和親坪哄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容