蒙惠者雖知其然,而未必知其所以然也。
寫了這么多,必須證明一下本書并不是一份乏味的使用文檔,我們來深入看看Sea.js,搞清楚它時(shí)如何工作的吧!
要想了解Sea.js的運(yùn)作機(jī)制,就不得不先了解其CMD規(guī)范。
Sea.js采用了和Node相似的CMD規(guī)范,我覺得它們應(yīng)該是一樣的。使用require、exports和module來組織模塊。但Sea.js比起Node的不同點(diǎn)在于,前者的運(yùn)行環(huán)境是在瀏覽器中,這就導(dǎo)致A依賴的B模塊不能同步地讀取過來,所以Sea.js比起Node,除了運(yùn)行之外,還提供了兩個(gè)額外的東西:
即Sea.js必須分為模塊加載期和執(zhí)行期。加載期需要將執(zhí)行期所有用到的模塊從服務(wù)端同步過來,在再執(zhí)行期按照代碼的邏輯順序解析執(zhí)行模塊。本身執(zhí)行期與node的運(yùn)行期沒什么區(qū)別。
所以Sea.js需要三個(gè)接口:
并不太喜歡Sea.js的use API,因?yàn)槠浠卣{(diào)函數(shù)并沒有使用與Define一樣的參數(shù)列表。
模塊id的標(biāo)準(zhǔn)參考Module Identifiers,簡單說來就是作為一個(gè)模塊的唯一標(biāo)識(shí)。
出于學(xué)習(xí)的目的,我將它們翻譯引用在這里:
顧名思義,factory就是工廠,一個(gè)可以產(chǎn)生模塊的工廠。node中的工廠就是新的運(yùn)行時(shí),而在Sea.js中(Tea.js中也同樣),factory就是一個(gè)函數(shù)。這個(gè)函數(shù)接受三個(gè)參數(shù)。
function (require, exports, module) {
// here is module body
}
在整個(gè)運(yùn)行時(shí)中只有模塊,即只有factory。
依賴就是一個(gè)id的數(shù)組,即模塊所依賴模塊的標(biāo)識(shí)。
有很多語言都有模塊化的結(jié)構(gòu),比如c/c++的#include
語句,Ruby的require
語句等等。模塊的執(zhí)行,必然需要其依賴的模塊準(zhǔn)備就緒才能順利執(zhí)行。
c/c++是編譯語言,在預(yù)編譯時(shí),替換#include
語句,將依賴的文件內(nèi)容包含進(jìn)來,在編譯后的執(zhí)行期,所有的模塊才會(huì)開始執(zhí)行;
而Ruby是解釋型語言,在模塊執(zhí)行前,并不知道它依賴什么模塊,待到執(zhí)行到require
語句時(shí),執(zhí)行將暫停,從外部讀取并執(zhí)行依賴,然后再回來繼續(xù)執(zhí)行當(dāng)前模塊。
JavaScript作為一門解釋型語言,在復(fù)雜的瀏覽器環(huán)境中,Sea.js是如何處理CMD模塊間的依賴的呢?
require
想要解釋這個(gè)問題,我們還是從Node模塊說起,node于Ruby類似,用我們之前使用過的一個(gè)模塊作為例子:
// File: usegreet.js
var greet = require("./greet");
greet.helloJavaScript();
當(dāng)我們使用node usegreet.js
來運(yùn)行這個(gè)模塊時(shí),實(shí)際上node會(huì)構(gòu)建一個(gè)運(yùn)行的上下文,在這個(gè)上下文中運(yùn)行這個(gè)模塊。運(yùn)行到require('./greet')
這句話時(shí),會(huì)通過注入的API,在新的上下文中解析greet.js這個(gè)模塊,然后通過注入的exports
或module
這兩個(gè)關(guān)鍵字獲取該模塊的接口,將接口暴露出來給usegreet.js使用,即通過greet
這個(gè)對(duì)象來引用這些接口。例如,helloJavaScript
這個(gè)函數(shù)。詳細(xì)細(xì)節(jié)可以參看node源碼中的
module.js。
node的模塊方案的特點(diǎn)如下:
實(shí)際上node如果通過fs異步的讀取文件的話,require也可以是異步的,所以曾經(jīng)node中有require.async這個(gè)API。
由于在瀏覽器端,采用與node同樣的依賴加載方式是不可行的,因?yàn)橐蕾囍挥性趫?zhí)行期才能知道,但是此時(shí)在瀏覽器端,我們無法像node一樣直接同步地讀取一個(gè)依賴文件并執(zhí)行!我們只能采用異步的方式。于是Sea.js的做法是,分成兩個(gè)時(shí)期——加載期和執(zhí)行期;
的確,我們可以使用同步的XHR從服務(wù)端加載依賴,但是本身就是單進(jìn)程的JavaScript還需要等待文件的加載,那性能將大打折扣。
不難想見,模塊間的依賴就像一棵樹。啟動(dòng)模塊作為根節(jié)點(diǎn),依賴模塊作為葉子節(jié)點(diǎn)。下面是pixelegos的依賴樹:
更多建議: