首頁  >  文章  >  web前端  >  用js實作簡易模組載入器的方法

用js實作簡易模組載入器的方法

一个新手
一个新手原創
2017-10-11 10:07:231583瀏覽


前言

前一段時間分析過require.js原始碼,整體的分析有些泛泛而,談內容有些空洞,沒有把握住requirejs依賴處理的精髓, 這是分析require.js源碼最失敗的地方。

雖然沒有真正的理解其實作細節,但是對其​​原始碼組織以及基本的邏輯執行有了整體的了解。

本文是參考網路上的源碼,分析其思想實現的簡易的模組載入器,旨在加深對於require.js的認知與理解。

實作想法

首先明確的幾點如下:

  • 每呼叫一次require函數,就會建立一個Context物件

  • Module物件表示模組對象,基本的操作都是該物件的原型方法

上面兩個物件是實作該簡易模組載入器核心,載入過程中的處理流程如下圖所示:
用js實作簡易模組載入器的方法
用js實作簡易模組載入器的方法代码

#Module物件的屬性有:

mid:表示模組id
 src:模組路徑
 name: 模組名稱
 deps:模組的依賴清單
 callback:回呼函數
 errback:錯誤處理函數
 status:模組狀態
 exports:與回呼函數參數序列對應的模組輸出

Module的原型物件有以下幾個方法:

init:Module物件的初始化處理
 fetch:建立script節點並追加到元素節點上
 checkCycle:處理循環依賴,傳回目前的循環依賴清單
 handleDeps:處理依賴清單
 changeStatus:改變模組的狀態,主要處理模組是否載入成功
execute:依賴的模組都載入成功後,參數清單的取得

如何處理依賴清單的

事實上,處理依賴清單是define以及require中處理,define函數以及require函數的處理程式碼如下:
用js實作簡易模組載入器的方法

首先看require函數,實例:

require(['a', 'b'], function(a, b) {
    console.log(a, b);
});

從上面可以看出,呼叫require函數,依賴列表是['a', 'b'],回呼函數是function(a, b) {console.log(a, b);}

從require程式碼可以就看出,呼叫Context建構函數建立一個Context對象,現在看下Context建構函數的處理:let Context = function(deps, callback, errback) {

    this.cid = ++contextId;
    this.init(deps, callback, errback);
}
;
    Context.prototype.init = function(deps, callback, errback) {
    this.deps = deps;
    this.callback = callback;
    this.errback = errback;
    contexts[this.cid] = this;
}
;

上面中重要的是contexts[this.cid] = this;將目前的Context物件註冊到全域contexts物件集合中。

然後呼叫handleDeps函數,該函數處理依賴列表,具體的程式碼如下:handleDeps: function() {

    let depCount = this.deps ? this.deps.length : 0;
    // require.js中处理循环依赖的处理 let requireInDep = (this.deps || []).indexOf('require');
    if (requireInDep !== -1) {
    depCount--;
    this.requireInDep = requireInDep;
    this.deps.splice(requireInDep, 1);
}
// 处理循环依赖情况 let cycleArray = this.checkCycle();
    if (cycleArray) {
    depCount = depCount - cycleArray.length;
}
// depCount表示当前模块的依赖模块数,depCount为0表示模块中某一依赖加载完成 this.depCount = depCount;
    if (depCount === 0) {
    this.execute();
    return;
}
// 遍历依赖列表,创建Module对象,并且将当前模块与其依赖的关系构建出来maps this.deps.forEach((depModuleName) => {
    if (!modules[depModuleName]) {
    let module = new Module(depModuleName);
    modules[module.name] = module;
}
if (!maps[depModuleName]) {
    maps[depModuleName] = [];
}
maps[depModuleName].push(this);
}
);

}循環依賴的處理

本次實作程式碼中的循環依賴的處理,是require.js中官方的方法,就是傳遞require在回呼函數中再次require,為什麼這樣就可以解決循環依賴?

就本次實作而言,因為require一次就會建立一個Context物件。主要程式碼如下:// require.js中處理循環依賴的處理let requireInDep = (this.deps || []).indexOf('require');
 

   if (requireInDep !== -1) {
    depCount--;
    this.requireInDep = requireInDep;
    this.deps.splice(requireInDep, 1);
}
// 获取循环依赖 let cycleArray = this.checkCycle();
    if (cycleArray) {
    depCount = depCount - cycleArray.length;
}
// execute函数中代码// 插入require到回调函数的参数列表中if (this.requireInDep !== -1 && this.requireInDep !== undefined) {
    arg.splice(this.requireInDep, 0, require);
}

結束語

紙上得來終覺淺,覺知此事要躬行,通過實現簡易的模組加載器,對於require.js模組加載的思想以及邏輯處理更加地清晰。

雖然與require.js對於js檔案的非同步載入的處理方式不同,但本質是一樣的,require.js是添加script節點到head標籤中,並且script添加async屬性來實現異步加載的。

#

以上是用js實作簡易模組載入器的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn