深入探尋seajs的模塊化與加載方式
由于一直在使用,所以了解了下seajs的源代碼。這里是我對下面幾個問題的理解:
1、seajs的require(XXX)的方法是怎樣實現(xiàn)模塊加載的?
2、為什么需要預加載?
3、為什么需要構(gòu)建工具?
4、構(gòu)建前后的代碼究竟有些什么區(qū)別,為什么要這么做?
問題1: seajs的require(XXX)的方法是怎樣實現(xiàn)模塊加載的?
代碼邏輯比較繞,對源代碼的理解放在文章的末尾,這里先簡單梳理下模塊加載的邏輯:
1、從seajs.use方法入口,開始加載use到的模塊。
2、use到的模塊這時mod緩存當中一定是不存在的。seajs創(chuàng)建一個新的mod,賦予一些初始的狀態(tài)。
3、執(zhí)行mod.load方法
4、一堆邏輯之后走到seajs.request方法,請求模塊文件。模塊加載完成之后,執(zhí)行define方法。
5、define方法分析提取模塊的依賴模塊,保存起來。緩存factory但不執(zhí)行。
6、模塊的依賴模塊再被加載,如果繼續(xù)有依賴模塊,則繼續(xù)加載。直至所有被依賴的模塊都加載完畢。
7、所有的模塊加載完畢之后,執(zhí)行use方法的callback.
8、模塊內(nèi)部邏輯從callback開始執(zhí)行。require方法在這個過程當中才被執(zhí)行。
問題2:為什么需要預加載?
我們看到seajs.use方法實際上是在所有依賴模塊都加載完了之后才執(zhí)行callback??梢岳斫獬稍跇I(yè)務邏輯代碼在執(zhí)行之前,必須先預加載所有被依賴的模塊代碼。那么為什么是一個這樣必須先做預加載的邏輯?
答案在于邏輯代碼里面引用其他模塊方法的這個require方法的執(zhí)行方法:
var mod = require(id);
這個語法決定了mod的取得是個同步執(zhí)行的過程,如果模塊代碼在此之前沒有被預加載的話,就只能采用異步加載回調(diào)的方法來實現(xiàn)了,那么整個seajs的執(zhí)行邏輯將完全會是另一個樣子。因為異步你會搞不懂模塊的執(zhí)行順序,邏輯會變的難以掌控。
問題3:為什么需要構(gòu)建工具?
可以看到?jīng)]有構(gòu)建前各個依賴模塊都是單獨加載的。這會產(chǎn)生過多的模塊請求,對于頁面的加載性能是不利的。構(gòu)建工具本質(zhì)上就是為了解決模塊合并加載的問題。
問題4:構(gòu)建前后的代碼究竟有些什么區(qū)別,為什么要這么做?
構(gòu)建工具究竟做了些什么。我們說它本質(zhì)上是為了解決代碼合并加載的問題,那么它所做的只是簡單的將各個模塊文件合并成一個文件?
當然不是。測試一下,你如果只是簡單把幾個模塊文件合并到一個文件以后,會發(fā)現(xiàn)這個文件根本沒有辦法正常執(zhí)行。
原因在于define方法的實現(xiàn)。
seajs是推崇定義模塊的時候只在define方法傳入factory參數(shù)的?;仡檇efine方法內(nèi)部,當沒有傳入id(姑且等同于模塊的url)時,會通過getCurrentScript()方法去取得當前正在執(zhí)行的這個模塊文件的url路徑,然后把這個路徑作為鍵值與模塊本身一起緩存到cachedMods。這里很關鍵的一點是,整個seajs內(nèi)部的這個模塊緩存機制其實是依賴每個模塊的url來做緩存的鍵值。require(id)方法,歸根結(jié)底也是通過url鍵值到。require(id)方法,歸根結(jié)底也是通過url鍵值到cachedMods里面去找相應的模塊。這個鍵值不能重復不能出錯,不然模塊的對應關系就混亂了。如果把a、b、c幾個模塊文件簡單合并到一個目標文件x之后,getCurrentScript()只能獲取到x的路徑,三個模塊的鍵值就沒法做出區(qū)別了,執(zhí)行肯定出錯。
所以如果要把幾個模塊文件合并在一起,就必須為每個模塊明確uri。也就是define方法必須都傳入id參數(shù)。當id傳入的時候,seajs會將這個id轉(zhuǎn)換為url用作緩存的鍵值。
如果只傳id和factory,也就是 define(id, factory),那么deps = undefined,define方法就會去執(zhí)行parseDependencies(factory.toString())方法提取factory里面的依賴模塊,后續(xù)會走到解析模塊路徑,線上單獨加載各個模塊的邏輯里面去,這個時候就失去了合并加載的意義了。
所以合并加載,define方法必須正確的傳入id,deps,factory三個參數(shù)才能正確執(zhí)行。
seajs 所謂CMD的模塊定義方法,是提倡大家寫模塊的階段都只傳factory一個參數(shù)的,其他兩個參數(shù)在后期代碼構(gòu)建的階段來生成。上面解釋了為什么這兩參數(shù)在構(gòu)建后是必須的。
至于為什么提倡定義模塊的時候只傳factory,我看主要是因為手工傳入的id和deps參數(shù),極易出錯,不便維護。工具可以提高效率并保證參數(shù)的正確。
附: 對seajs 主要代碼邏輯的理解。
說明:源代碼版本是Sea.js 2.3.0
1、先看看define方法做了些什么
Module.define = function (id, deps, factory)
define方法的時候,支持三個參數(shù)。其中id,deps是選填的。factory必須。代碼里面通過以下邏輯來控制:
但其實deps是必須的,因為seajs必須知道每個模塊依賴了哪些模塊,不然無法執(zhí)行加載。
所以,當factory是函數(shù),并且deps沒有被主動傳入的時候,就需要使用parseDependencies方法來分析出factory當中的依賴模塊了。
parseDependencies方法做的事情主要就是用一個正則表達式把函數(shù)體里面所有require(XXX)里面的XXX提取出來,這也就是這個函數(shù)依賴到的所有模塊了。
方法本身不復雜,但是這個正則表達式不簡單:
分析完deps之后,將模塊定義存入緩存:
注意,我們會發(fā)現(xiàn)define方法純粹只是分析模塊、存儲模塊,并沒有執(zhí)行模塊。
2、真正執(zhí)行模塊,是在require方法里面。我們接下來看require。
簡而言之require方法就是根據(jù)id在define定義存儲的模塊緩存中找到相應的模塊,并執(zhí)行它,獲得模塊定義返回的方法:
整個這個大步驟中,有一個很關鍵的步驟,有必要細說:
Module.get(require.resolve(id))。
require一個模塊的時候,首先要找到這個模塊。 Module.get方法就起這個作用。
cachedMods里面沒有的話,就創(chuàng)建一個新的Module并緩存到cachedMods里面:
define和rquire方法這樣看來不算復雜。seajs主要還是模塊加載的邏輯有點復雜。
3、seajs真正執(zhí)行的入口,是use方法:
通過use方法,從這里的ids開始觸發(fā)模塊的加載和執(zhí)行。
可以看到加載的關鍵點在mod.load方法。
load方法代碼有點長,其中的主要邏輯是:判斷mod的當前狀態(tài)是否為已加載或者加載中。
在Module的舒適化函數(shù)中,我們可以看到status默認值是0.
所以沒有加載過的新模塊,到這里都是: mod.status = STATUS.LOADING 狀態(tài)設置為加載中,并執(zhí)行后續(xù)加載邏輯。
接來下是獲取模塊的依賴urls
mod.resolve方法:
Module.resolve方法本質(zhì)上就是把相對路徑、配置的path、別名等轉(zhuǎn)換成一個絕對路徑。不貼代碼了。
更新模塊加載狀態(tài)。
加載模塊的邏輯:
主要是m.fetch方法,里面其他邏輯這里略過。
可以看到 seajs.request最終會去執(zhí)行模塊文件的加載:
當所有依賴模塊加載完了之后,執(zhí)行mod的onload方法
這里是 mod.onload()方法
到此,seajs的核心邏輯就差不多都看到了。供參考,有理解不到位或者表達不準確的地方,歡迎一起探討。
以上所述就是本文的全部內(nèi)容了,希望大家能夠喜歡。
- 在Html中使用Requirejs進行模塊化開發(fā)實例詳解
- 使用requirejs模塊化開發(fā)多頁面一個入口js的使用方式
- 詳解JavaScript模塊化開發(fā)
- JavaScript的模塊化開發(fā)框架Sea.js上手指南
- Seajs 簡易文檔 提供簡單、極致的模塊化開發(fā)體驗
- JavaScript模塊化開發(fā)之SeaJS
- 了解Javascript的模塊化開發(fā)
- javascript 構(gòu)建模塊化開發(fā)過程解析
- seajs和requirejs模塊化簡單案例分析
- 基于RequireJS和JQuery的模塊化編程日常問題解析
- 基于RequireJS和JQuery的模塊化編程——常見問題全面解析
- JavaScript 模塊化開發(fā)實例詳解【seajs、requirejs庫使用】
相關文章
seajs中模塊的解析規(guī)則詳解和模塊使用總結(jié)
這篇文章主要介紹了seajs中模塊的解析規(guī)則詳解和模塊使用總結(jié),需要的朋友可以參考下2014-03-03