前言:
為什么我們需要模塊化開發,模塊化開發的好處有哪些? 首先我們先說一下非模塊化的開發方式帶來的弊端。 非模塊化開發中會導致一些問題的出現,變量和函數命名可能相同,會造成變量污染和沖突,並且出錯時候很難排查。耦合程度高,不符合軟件開發中的高內聚和低耦合的原則,所以我們就可以總結一下模塊化開發的好處了:
① 解決項目中的變量污染問題。
② 開發效率高,有利於多人協同開發。
③ 職責單一,方便代碼重用和維護 。
③ 解決文件依賴問題,無需關注引包順序 。
模塊化開發的演變過程
- 普通的函數封裝
- 封裝成對象
- 私有公有成員分離 (使用自執行函數,避免變量污染)
- 模塊的維護和擴展(用多個自執行函數把模塊分離開來,使用開閉原則—去增添功能,而盡量不要修改原來的代碼,)
- 可以添加模塊的第三方依賴(比如添加jQuery的$,$作為一個編程的接口,降低程序之間的耦合度)
模塊化的規范
服務端規范 — CommonJs
以為作為服務器端的開發,不會經過網絡傳輸,所以包的加載幾乎都是瞬間完成的,只有加載完成了,才能執行操作,所以commonjs是同步的,nodejs就是實現了這種規范。
有關規范的具體詳情,找了兩篇文章:
http://www.open-open.com/doc/view/f7df10bb81c347f79b436faa85dcfd81
http://blog.jobbole.com/49290/
瀏覽器端規范 — AMD 和 CMD
瀏覽器端的規范,因為所有的文件請求都要經過網絡傳輸,很多文件的加載會有不確定因素存在為了解決這個問題,瀏覽器端出來了這兩個規范。
AMD規范:
異步模塊定義規范(Asynchronous Module Definition)制定了定義模塊的規則,這樣模塊和模塊的依賴可以被異步加載。這和瀏覽器的異步加載模塊的環境剛好適應(瀏覽器同步加載模塊會導致性能、可用性、調試和跨域訪問等問題)。具體規范鏈接在這里:中文版 英文版 ,requireJs就是AMD規范的實現。
-
requirejs的基本使用:
① 引入requirejs包,並且設置入口文件<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文件中
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, 詳細資料還得戳旁邊的資料文檔。
-
seajs 的基本使用
-
引入包文件
<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:
-
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
-
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
-
module1.js文件中的代碼:
console.log('我是module1文件內的');- 1
- 2
- 1
- 2
-
在Chrome下測試,最后控制台的輸出結果:
我是module1文件內的 非IE瀏覽器 回調內的函數最后執行- 1
- 2
- 3
- 1
- 2
- 3
-
總結:在模塊加載的過程中,先執行依賴模塊內的邏輯,最后再去執行回調函數內的邏輯
-
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 進行配置,讓模塊編寫、開發調試更方便。
- https://github.com/seajs/seajs/issues/262
-
seajs 中的module
- 看 API 中的module Object
Seajs和RequireJS的區別
— 引自 玉伯
- 兩者定位有差異。RequireJS 想成為瀏覽器端的模塊加載器,同時也想成為 Rhino / Node 等環境的模塊加載器。SeaJS 則專注於 Web 瀏覽器端,同時通過 Node 擴展的方式可以很方便跑在 Node 服務器端。
- 兩者遵循的標准有差異。RequireJS 遵循的是 AMD(異步模塊定義)規范,SeaJS 遵循的是 CMD (通用模塊定義)規范。規范的不同,導致了兩者 API 的不同。SeaJS 更簡潔優雅,更貼近 CommonJS Modules/1.1 和 Node Modules 規范。
- 兩者社區理念有差異。RequireJS 在嘗試讓第三方類庫修改自身來支持 RequireJS,目前只有少數社區采納。SeaJS 不強推,而采用自主封裝的方式來“海納百川”,目前已有較成熟的封裝策略。
- 兩者對調試等的支持有差異。SeaJS 通過插件,可以實現 Fiddler 中自動映射的功能,還可以實現自動 combo 等功能,非常方便便捷。RequireJS 無這方面的支持。
- 兩者的插件機制有差異。RequireJS 采取的是在源碼中預留接口的形式,源碼中留有為插件而寫的代碼。SeaJS 采取的插件機制則與 Node 的方式一致:開放自身,讓插件開發者可直接訪問或修改,從而非常靈活,可以實現各種類型的插件。插件開發者可直接訪問或修改,從而非常靈活,可以實現各種類型的插件。
- 對於依賴的模塊,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)。CMD 推崇 as lazy as possible.
- CMD 推崇依賴就近,AMD 推崇依賴前置。
