自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JavaScript 模塊化及 SeaJs 源碼分析

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

網頁的結構越來越復雜,簡直可以看做一個簡單APP,如果還像以前那樣把所有的代碼都放到一個文件里面會有一些問題:

  • 全局變量互相影響

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

  • 結構混亂、很難維護

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

基本原理

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

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

這樣做的壞處是:把模塊中所有的成員都暴露了!我們知道函數(shù)的本地變量是沒法從外面進行訪問的,那么可以用立即執(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ī)范來,那好處會非常大:可以互相引用!

模塊規(guī)范

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

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

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

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

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

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

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

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

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

as lazy as possible!

對于依賴的模塊的處理方式和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源碼分析

剛接觸模塊化的時候感覺這個太簡單了,不就是:

創(chuàng)建script標簽的時候設置一下onload和src!

事實上是這樣的,但也不完全是!下面來開始看SeaJS的代碼(sea-debug.js)。一個模塊在加載的過程中可能經歷下面幾種狀態(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 

內存中用Modul對象來維護模塊的信息:

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

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

  1. seajs.use(‘./main’, function(main) {// 依賴及回調方法 
  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.     } 

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

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

換種說法:

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

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

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

有幾個細節(jié):

  1. 已經加載完成的模塊不會被傳播;

  2. 已經傳播過一次的模塊不會再次傳播;

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

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

  1. importScripts

  2. script

然后根據結果執(zhí)行load或者error方法。依賴的所有模塊都加載完成后就會執(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 

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

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

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

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

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

  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) 

當你寫的模塊代碼中require時,每次都會執(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() 

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

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

總結

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

責任編輯:王雪燕 來源: WsztRush
相關推薦

2019-08-28 16:18:39

JavaScriptJS前端

2017-04-10 14:23:01

typescriptjavascriptwebpack

2013-08-20 18:39:34

JavaScript模requireJS

2020-10-09 06:40:53

惡意軟件

2010-05-21 18:26:58

2020-05-12 08:39:50

JavaScript工具技術

2013-08-20 15:31:18

前端模塊化

2017-05-18 10:23:55

模塊化開發(fā)RequireJsJavascript

2020-09-17 10:30:21

前端模塊化組件

2015-10-10 11:29:45

Java模塊化系統(tǒng)初探

2020-09-18 09:02:32

前端模塊化

2022-03-11 13:01:27

前端模塊

2010-01-12 16:15:02

模塊化交換機

2018-03-21 21:31:28

Java9編程Java

2022-09-05 09:01:13

前端模塊化

2018-06-21 09:36:09

模塊化數(shù)據中心集中化

2019-09-02 10:51:59

Python腳本語言程序員

2017-05-18 11:43:41

Android模塊化軟件

2016-10-09 11:03:41

Javascript模塊化Web

2016-12-21 17:02:35

數(shù)據中心MDC模塊化
點贊
收藏

51CTO技術棧公眾號