JavaScript中的模塊化之AMD和CMD

前言: 
爲何咱們須要模塊化開發,模塊化開發的好處有哪些? 首先咱們先說一下非模塊化的開發方式帶來的弊端。 非模塊化開發中會致使一些問題的出現,變量和函數命名可能相同,會形成變量污染和衝突,而且出錯時候很難排查。耦合程度高,不符合軟件開發中的高內聚和低耦合的原則,因此咱們就能夠總結一下模塊化開發的好處了: 
① 解決項目中的變量污染問題。 
② 開發效率高,有利於多人協同開發。 
③ 職責單一,方便代碼重用和維護 。 
③ 解決文件依賴問題,無需關注引包順序 。javascript

模塊化開發的演變過程

  • 普通的函數封裝
  • 封裝成對象
  • 私有公有成員分離 (使用自執行函數,避免變量污染)
  • 模塊的維護和擴展(用多個自執行函數把模塊分離開來,使用開閉原則—去增添功能,而儘可能不要修改原來的代碼,)
  • 能夠添加模塊的第三方依賴(好比添加jQuery的$,$做爲一個編程的接口,下降程序之間的耦合度)

模塊化的規範

服務端規範 — CommonJs

覺得做爲服務器端的開發,不會通過網絡傳輸,因此包的加載幾乎都是瞬間完成的,只有加載完成了,才能執行操做,因此commonjs是同步的,nodejs就是實現了這種規範。html

有關規範的具體詳情,找了兩篇文章: 
http://www.open-open.com/doc/view/f7df10bb81c347f79b436faa85dcfd81 
http://blog.jobbole.com/49290/java

瀏覽器端規範 — AMD 和 CMD

瀏覽器端的規範,由於全部的文件請求都要通過網絡傳輸,不少文件的加載會有不肯定因素存在爲了解決這個問題,瀏覽器端出來了這兩個規範。node

AMD規範:

異步模塊定義規範(Asynchronous Module Definition)制定了定義模塊的規則,這樣模塊和模塊的依賴能夠被異步加載。這和瀏覽器的異步加載模塊的環境恰好適應(瀏覽器同步加載模塊會致使性能、可用性、調試和跨域訪問等問題)。具體規範連接在這裏:中文版   英文版 ,requireJs就是AMD規範的實現。jquery

  • requirejs的基本使用: 
    ① 引入requirejs包,而且設置入口文件git

    <script data-main='js/main' src='http://apps.bdimg.com/libs/require.js/2.1.9/require.min.js'></script> 
    • 1
    • 2
    • 1
    • 2

    ② 定義主模塊 在main.js文件中github

    define(['module1','module2',function(m1,m2) { // 說明:上面兩個module是依賴模塊,經過m1,m2的方式來使用 // 在這裏咱們寫本身的業務邏輯 todo // 若是咱們須要暴露出去,須要 return ,不是用exports或者module.exports // return {}; }]);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    後續的使用,直接看文檔吧,之後有機會再來補充。 
    requirejs官網 
    requirejs中文官網編程

CMD規範:

通用模塊定義 (Common Module Definition) 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。相似的還有 CommonJS Modules/2.0 規範。 
這些規範的目的都是爲了 JavaScript 的模塊化開發,特別是在瀏覽器端的。 
目前這些規範的實現都能達成瀏覽器端模塊化開發的目的。有兩篇文檔相關文檔: 文檔1   文檔2 , 下面簡要說一下seajs, 詳細資料還得戳旁邊的資料文檔。json

  • seajs 的基本使用api

    • 引入包文件

      <script src='http://apps.bdimg.com/libs/seajs/2.3.0/sea.js'></script> 
      • 1
      • 2
      • 1
      • 2
    • 定義一個模塊,好比在這個文件 student.js 中

      define(function(require,exports,module){ function Student(){ this.name = '張三'; } // 對外暴露該模塊的接口: module.exports = new Student(); })
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 使用模塊:經過 seajs.use() 來實現,第一個參數是使用模塊的路徑,第二個回調函數中的參數是所要使用模塊暴露出來的一個接口。

      seajs.use('./student.js',function(stu){ console.log(stu.name); // 張三 }); 
      • 1
      • 2
      • 3
      • 4
      • 1
      • 2
      • 3
      • 4
  • seajs 暴露出接口的方式

    • 經過 module.exports 方式導出對象
    • 經過 exports.屬性或方法 方式導出
    • 二者的區別和聯繫:module.exports 和exports 指向的是同一個對象。exports是函數內的一個形式參數,而module.exports是一個實實在在的對象,通常在導出的時候用 module.exports 指向一個新的對象,而最終模塊都是用module.exports來導出的。
  • seajs 內部的異步加載簡單小demo — 寫個小demo來闡述一下這個異步加載機制,在demo中,有如下幾個文件 index.html ,loadJs.js ,module1.js:

    1. index.html文件中的關鍵代碼

      <!--引入cdn上的seajs文件--> <script src='http://apps.bdimg.com/libs/seajs/2.3.0/sea.js'></script> <script src='loadJs.js'></script> <script> loadJs('module1.js',function(){ console.log('回調內的函數最後執行'); }); </script> 
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    2. loadJs.js文件中的代碼

      function loadJs(path,callback) { var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.setAttribute('src',path); head.appendChild(script); if(!/*@cc_on!*/false) { // 非IE瀏覽器 script.onload = function() { console.log('非IE瀏覽器'); callback(); } }else{ script.onreadystatechange = function(){ if(script.readyState === 'loaded' || script.readyState === 'complete') { console.log('IE'); callback(); } } } }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
    3. module1.js文件中的代碼:

      console.log('我是module1文件內的'); 
      • 1
      • 2
      • 1
      • 2
    4. 在Chrome下測試,最後控制檯的輸出結果:

      我是module1文件內的
      非IE瀏覽器
      回調內的函數最後執行
      • 1
      • 2
      • 3
      • 1
      • 2
      • 3
    5. 總結:在模塊加載的過程當中,先執行依賴模塊內的邏輯,最後再去執行回調函數內的邏輯

      • seajs 異步加載 require 中的async方法:

        require.async() 方法用來在模塊內部異步加載模塊,並在加載完成後執行指定回調。callback 參數可選。 require 是同步往下執行,require.async 則是異步回調執行。require.async 通常用來加載可延遲異步加載的模塊。

      • seajs 的第三方依賴庫的引入:好比 jQuery,咱們須要使用jquery,加入到咱們的項目中,首先咱們須要對jquery 進行改造,在高版本的jquery文件好比2.2中支持amd規範但不支持cmd規範,在文件中的最後找到以下代碼:

        if ( typeof define === "function" && define.amd ) { define( "jquery", [], function() { return jQuery; } ); }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 1
        • 2
        • 3
        • 4
        • 5

        咱們把它改造爲:

        if ( typeof define === "function" && (define.amd || define.cmd) ) { define( "jquery", [], function() { return jQuery; } ); }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 1
        • 2
        • 3
        • 4
        • 5

        而後咱們能夠經過加載模塊的方式,把jQuery加載進去了。

  • seajs的配置調試:

    • https://github.com/seajs/seajs/issues/262 
      API上講的很詳細。
    • 簡單的寫一寫:

      seajs.config({
        // 別名配置 alias: { 'es5-safe': 'gallery/es5-safe/0.9.3/es5-safe', 'json': 'gallery/json/1.0.2/json', 'jquery': 'jquery/jquery/1.10.1/jquery' }, // 路徑配置 paths: { 'gallery': 'https://a.alipayobjects.com/gallery' } })
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    • 經過對 sea.js 進行配置,讓模塊編寫、開發調試更方便。

  • seajs 中的module

    • 看 API 中的module Object

Seajs和RequireJS的區別

— 引自 玉伯

  1. 二者定位有差別。RequireJS 想成爲瀏覽器端的模塊加載器,同時也想成爲 Rhino / Node 等環境的模塊加載器。SeaJS 則專一於 Web 瀏覽器端,同時經過 Node 擴展的方式能夠很方便跑在 Node 服務器端。
  2. 二者遵循的標準有差別。RequireJS 遵循的是 AMD(異步模塊定義)規範,SeaJS 遵循的是 CMD (通用模塊定義)規範。規範的不一樣,致使了二者 API 的不一樣。SeaJS 更簡潔優雅,更貼近 CommonJS Modules/1.1 和 Node Modules 規範。
  3. 二者社區理念有差別。RequireJS 在嘗試讓第三方類庫修改自身來支持 RequireJS,目前只有少數社區採納。SeaJS 不強推,而採用自主封裝的方式來「海納百川」,目前已有較成熟的封裝策略。
  4. 二者對調試等的支持有差別。SeaJS 經過插件,能夠實現 Fiddler 中自動映射的功能,還能夠實現自動 combo 等功能,很是方便便捷。RequireJS 無這方面的支持。
  5. 二者的插件機制有差別。RequireJS 採起的是在源碼中預留接口的形式,源碼中留有爲插件而寫的代碼。SeaJS 採起的插件機制則與 Node 的方式一致:開放自身,讓插件開發者可直接訪問或修改,從而很是靈活,能夠實現各類類型的插件。插件開發者可直接訪問或修改,從而很是靈活,能夠實現各類類型的插件。
  6. 對於依賴的模塊,AMD 是提早執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改爲能夠延遲執行(根據寫法不一樣,處理方式不一樣)。CMD 推崇 as lazy as possible.
  7. CMD 推崇依賴就近,AMD 推崇依賴前置。
相關文章
相關標籤/搜索