日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
JavaScript模塊化及SeaJs源碼分析

網(wǎng)頁的結(jié)構(gòu)越來越復(fù)雜,簡直可以看做一個(gè)簡單APP,如果還像以前那樣把所有的代碼都放到一個(gè)文件里面會(huì)有一些問題:

創(chuàng)新互聯(lián)總部坐落于成都市區(qū),致力網(wǎng)站建設(shè)服務(wù)有做網(wǎng)站、成都網(wǎng)站制作、網(wǎng)絡(luò)營銷策劃、網(wǎng)頁設(shè)計(jì)、網(wǎng)站維護(hù)、公眾號(hào)搭建、微信平臺(tái)小程序開發(fā)、軟件開發(fā)等為企業(yè)提供一整套的信息化建設(shè)解決方案。創(chuàng)造真正意義上的網(wǎng)站建設(shè),為互聯(lián)網(wǎng)品牌在互動(dòng)行銷領(lǐng)域創(chuàng)造價(jià)值而不懈努力!

  • 全局變量互相影響

  • JavaScript文件變大,影響加載速度

  • 結(jié)構(gòu)混亂、很難維護(hù)

和后端(比如Java)比較就可以看出明顯的差距。2009年Ryan Dahl創(chuàng)建了node.js項(xiàng)目,將JavaScript用于服務(wù)器編程,這標(biāo)志“JS模塊化編程”正式誕生。

基本原理

模塊就是一些功能的集合,那么可以將一個(gè)大文件分割成一些小文件,在各個(gè)文件中定義不同的功能,然后在HTML中引入:

 
 
  1. var module1 = new Object({
  2.     _count : 0,
  3.     m1 : function (){
  4.         //...
  5.     },
  6.     m2 : function (){
  7.         //...
  8.     }
  9. });

這樣做的壞處是:把模塊中所有的成員都暴露了!我們知道函數(shù)的本地變量是沒法從外面進(jìn)行訪問的,那么可以用立即執(zhí)行函數(shù)來優(yōu)化:

 
 
  1. var module1 = (function(){
  2.     var _count = 0;
  3.     var m1 = function(){
  4.         //...
  5.     };
  6.     var m2 = function(){
  7.         //...
  8.     };
  9.     return {
  10.         m1 : m1, m2 : m2
  11.     };
  12. })();

大家定義模塊的方式可能五花八門,如果都能按照一定的規(guī)范來,那好處會(huì)非常大:可以互相引用!

模塊規(guī)范

在node.js中定義math.js模塊如下:

 
 
  1. function add(a, b){
  2.     return a + b;
  3. }
  4. exports.add = add;

在其他模塊中使用的時(shí)候使用全局require函數(shù)加載即可:

 
 
  1. var math = require('math');
  2. math.add(2,3);

在服務(wù)器上同步require是沒有問題的,但是瀏覽器在網(wǎng)絡(luò)環(huán)境就不能這么玩了,于是有了異步的AMD規(guī)范:

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

模塊的定義方式如下(模塊可以依賴其他的模塊):

 
 
  1. define(function (){ // define([module], callback);
  2.     var add = function (x,y){
  3.         return x+y;
  4.     };
  5.     return { add: add };
  6. });

用RequireJS可以加載很多其他資源(看這里),很好很強(qiáng)大!在工作中用的比較多的是SeaJS,所使用的規(guī)范稱為CMD,推崇(應(yīng)該是指異步模式):

as lazy as possible!

對(duì)于依賴的模塊的處理方式和AMD的區(qū)別在于:

AMD是提前執(zhí)行(依賴前置),CMD是延遲執(zhí)行(依賴就近)。

在CMD中定義模塊的方式如下:

 
 
  1. define(function(require, exports, module) {
  2.     var a = require('./a');
  3.     a.doSomething();
  4.     var b = require('./b');
  5.     b.doSomething();
  6. });

使用方式直接看文檔,這里就不贅述了!

SeaJS源碼分析

剛接觸模塊化的時(shí)候感覺這個(gè)太簡單了,不就是:

創(chuàng)建script標(biāo)簽的時(shí)候設(shè)置一下onload和src!

事實(shí)上是這樣的,但也不完全是!下面來開始看SeaJS的代碼(sea-debug.js)。一個(gè)模塊在加載的過程中可能經(jīng)歷下面幾種狀態(tài):

 
 
  1. var STATUS = Module.STATUS = {
  2.     // 1 - The `module.uri` is being fetched
  3.     FETCHING: 1,
  4.     // 2 - The meta data has been saved to cachedMods
  5.     SAVED: 2,
  6.     // 3 - The `module.dependencies` are being loaded
  7.     LOADING: 3,
  8.     // 4 - The module are ready to execute
  9.     LOADED: 4,
  10.     // 5 - The module is being executed
  11.     EXECUTING: 5,
  12.     // 6 - The `module.exports` is available
  13.     EXECUTED: 6,
  14.     // 7 - 404
  15.     ERROR: 7
  16. }

內(nèi)存中用Modul對(duì)象來維護(hù)模塊的信息:

 
 
  1. function Module(uri, deps) {
  2.     this.uri = uri
  3.     this.dependencies = deps || [] // 依賴模塊ID列表
  4.     this.deps = {} // 依賴模塊Module對(duì)象列表
  5.     this.status = 0 // 狀態(tài)
  6.     this._entry = [] // 在模塊加載完成之后需要調(diào)用callback的模塊
  7. }

在頁面上啟動(dòng)模塊系統(tǒng)需要使用seajs.use方法:

 
 
  1. seajs.use(‘./main’, function(main) {// 依賴及回調(diào)方法
  2.     main.init();
  3. });

加載過程的整體邏輯可以在Module.prototype.load中看到:

 
 
  1. Module.prototype.load = function() {
  2.     var mod = this
  3.     if (mod.status >= STATUS.LOADING) {
  4.         return
  5.     }
  6.     mod.status = STATUS.LOADING
  7.     var uris = mod.resolve() // 解析依賴模塊的URL地址
  8.     emit("load", uris)
  9.     for (var i = 0, len = uris.length; i < len; i++) {
  10.         mod.deps[mod.dependencies[i]] = Module.get(uris[i])// 從緩存取或創(chuàng)建
  11.     }
  12.     mod.pass(); // 將entry傳遞給依賴的但還沒加載的模塊
  13.     if (mod._entry.length) {// 本模塊加載完成
  14.         mod.onload()
  15.         return
  16.     }
  17.     var requestCache = {};
  18.     var m;
  19.     // 加載依賴的模塊
  20.     for (i = 0; i < len; i++) {
  21.         m = cachedMods[uris[i]]
  22.         if (m.status < STATUS.FETCHING) {
  23.             m.fetch(requestCache)
  24.         } else if (m.status === STATUS.SAVED) {
  25.             m.load()
  26.         }
  27.     }
  28.     for (var requestUri in requestCache) {
  29.         if (requestCache.hasOwnProperty(requestUri)) {
  30.             requestCache[requestUri]()
  31.         }
  32.     }
  33. }

總體上邏輯很順就不講了,唯一比較繞的就是_entry數(shù)組了。網(wǎng)上沒有找到比較通俗易懂的文章,于是看著代碼連蒙帶猜地大概看懂了,其實(shí)只要記住它的目標(biāo)即可:

當(dāng)依賴的所有模塊加載完成后執(zhí)行回調(diào)函數(shù)!

換種說法:

數(shù)組_entry中保存了當(dāng)前模塊加載完成之后、哪些模塊的依賴可能加載完成的列表(依賴的反向關(guān)系)!

舉個(gè)例子,模塊A依賴于模塊B、C、D,那么經(jīng)過pass之后的狀態(tài)如下:

此時(shí)A中的remain為3,也就是說它還有三個(gè)依賴的模塊沒有加載完成!而如果模塊B依賴模塊E、F,那么在它load的時(shí)候會(huì)將A也傳遞出去:

有幾個(gè)細(xì)節(jié):

  1. 已經(jīng)加載完成的模塊不會(huì)被傳播;

  2. 已經(jīng)傳播過一次的模塊不會(huì)再次傳播;

  3. 如果依賴的模塊正在加載那么會(huì)遞歸傳播;

維護(hù)好依賴關(guān)系之后就可以通過Module.prototype.fetch來加載模塊,有兩種sendRequest的實(shí)現(xiàn)方式:

  1. importScripts

  2. script

然后根據(jù)結(jié)果執(zhí)行load或者error方法。依賴的所有模塊都加載完成后就會(huì)執(zhí)行onload方法:

 
 
  1. Module.prototype.onload = function() {
  2.     var mod = this
  3.     mod.status = STATUS.LOADED
  4.     for (var i = 0, len = (mod._entry || []).length; i < len; i++) {
  5.         var entry = mod._entry[i]
  6.         if (--entry.remain === 0) {
  7.             entry.callback()
  8.         }
  9.     }
  10.     delete mod._entry
  11. }

其中--entry.remain就相當(dāng)于告訴entry對(duì)應(yīng)的模塊:你的依賴列表里面已經(jīng)有一個(gè)完成了!而entry.remain === 0則說明它所依賴的所有的模塊都已經(jīng)加載完成了!那么此時(shí)將執(zhí)行回調(diào)函數(shù):

 
 
  1. for (var i = 0, len = uris.length; i < len; i++) {
  2.     exports[i] = cachedMods[uris[i]].exec();
  3. }
  4. if (callback) {
  5.     callback.apply(global, exports)// 執(zhí)行回調(diào)函數(shù)
  6. }

腳本下載完成之后會(huì)馬上執(zhí)行define方法來維護(hù)模塊的信息:

沒有顯式地指定dependencies時(shí)會(huì)用parseDependencies來用正則匹配方法中的require()片段(指定依賴列表是個(gè)好習(xí)慣)。

接著執(zhí)行factory方法來生成模塊的數(shù)據(jù):

 
 
  1. var exports = isFunction(factory) ?
  2.     factory.call(mod.exports = {}, require, mod.exports, mod) :
  3.     factory

然后執(zhí)行你在seajs.use中定義的callback方法:

 
 
  1. if (callback) {
  2.     callback.apply(global, exports)
  3. }

當(dāng)你寫的模塊代碼中require時(shí),每次都會(huì)執(zhí)行factory方法:

 
 
  1. function require(id) {
  2.     var m = mod.deps[id] || Module.get(require.resolve(id))
  3.     if (m.status == STATUS.ERROR) {
  4.         throw new Error('module was broken: ' + m.uri)
  5.     }
  6.     return m.exec()
  7. }

到這里核心的邏輯基本上講完了,補(bǔ)一張狀態(tài)的轉(zhuǎn)換圖:

以后在用的時(shí)候就可以解釋一些詭異的問題了!

總結(jié)

模塊化非常好用,因此在ECMAScript 6中也開始支持,但是瀏覽器支持還是比較堪憂的~~


本文標(biāo)題:JavaScript模塊化及SeaJs源碼分析
文章起源:http://www.dlmjj.cn/article/dppdces.html