微信小程式的require機制淺析

半個哈欠發表於2016-11-24

(注: 本文中所列微信小程式工具程式碼,並非為微信小程式原始程式碼,而是學習歸納的示意程式碼)

在學習開發微信小程式中, 分析總結了最近版本微信小程式模組化的函式 require的載入與初始化模組機制,
歸納說來,小程式JS模組載入可分為兩大步驟:
一,JS模組載入
二,JS模組初始化
具體如下:

一, JS模組載入:一次性載入全部JS, 但並不一定立即執行.

先提一提微信小程式架構: 類瀏覽器 -> HTTP本地服務 -> 雲端服務

微信小程式執行的架構,基本上是瀏覽器 -> HTTP本地服務 -> 雲端服務, HTTP本地服務用來讀取本地檔案或者代理雲端的檔案資源。
讀取專案中JS檔案, 是由HTTP本地服務取本地儲存的指令碼檔案.

似乎比較簡單,一個HTML 引用所有JS檔案

既然採用了這種架構,那微信小程式就類似瀏覽器那樣,藉助一個HTML頁面來引用載入所有的JS檔案。(注:這同NODE.JS的方式區別)
在小程式開發開具的HTTP服務部分程式碼,可以看到這個服務幹了這件事情:
微信小程式包目錄下面所有.js檔案, 會按<script src=”../xxx.js”> 方式插入生成一個HTML檔案,然後類似瀏覽器方式載入.

讓HTTP本地服務配合,對JS檔案作的包裝手法

可是事情並未結束,這種方式一載入,所有js檔案都會立即執行,亂糟糟生成一團,怎麼可能..那require函式又拿來幹什麼呢?
原來這兒,HTTP服務在返回.JS檔案內容的,給指令碼內容包裝上了一層: define函式

代理服務部分程式碼:
(projectManager.js)
function getScripts(projInfo, callback) {
  ...
    fs.readFile(fname, `utf8`, function(err, scripts) {
        ....    
        scripts = `define("` + moduleName + `", function(require, module, exports, ` + noBrowserStr +
        `){ ` + scripts + `
});`,
        needRequire && (scripts += `require("` + moduleName + `")`), //page頁面js檔案,會新增上require自己,載入後立即初始化。
        .....
        callback(null, scripts) //scripts串內容作為HTTP GET的返回

define函式非常簡單,大致如下:

......
    var 
    ......
    moduleList = {}; 
    define = function(moduleName, factory) {  //define是全域性函式,每個JS檔案都預設會呼叫. 
        moduleList[moduleName] = { status: status1, factory: factory }
    };

從上面程式碼看出,,這樣一來,每載入一個JS檔案,只是將其檔名與指令碼內容串加入了記憶體中的一個變數儲存,並未執行。 注意,這就與普通的HTML 指令碼引用載入立即執行完全不同了.

接下來,就輪到微信小程式的require函式出場了。

二, JS模組初始化:按需遞迴式require初始化

先看看微信小程式require函式的定義:

....
    require = function(moduleName) {       
        ....
        var module = moduleList[moduleName]; //define函式呼叫時為moduleList賦的值
        .....
        if (module.status === status1) {  
            //如果未初始化,則初始化
            var factory = module.factory,  //這個factory就是這個JS檔案的指令碼.
            obj = { exports: {} }, u = void 0;
            factory && (u = factory(o(moduleName), obj, obj.exports)), module.exports = obj.exports || u, module.status = status2
        }
        return module.exports
    }

從上面可以看出, require函式只是通過模組名,從記憶體中獲取指令碼內容執行,並置標誌以保證只執行一次.

再精簡一下:

require --呼叫-> factory --->模組中可能再require另一個模組...

這樣就是一個典型的遞迴結構。

三,補充一下:頁面js 其實也是被require函式載入

所謂頁面JS,,就是在app.json中註冊的page的js, 它們並沒有被其它JS require方式引用,
那麼它們在什麼時候初始化?
回到之前本地代理伺服器的程式碼,留意下面一點:

代理服務部分程式碼:
(projectManager.js)
function getScripts(projInfo, callback) {
  ...
    fs.readFile(fname, `utf8`, function(err, scripts) {
        ....    
        //page頁面js檔案,needRequire值為TRUE,會新增上require自己
        needRequire && (scripts += `require("` + moduleName + `")`), 
        .....

原來它們還是使用require函式初始化,而且是載入後立即執行。

目前通常微信小程式程式碼結構不會太複雜,但隨著產品的發展,需求的增加, 程式碼結構可能越來越複雜,越來越注意模組化.
同時,如何將舊有JS模組在微信小程式中重用,這也是個重要話題。 所以深入理解微信小程式的JS模組化機制也是很有價值的.

相關文章