动态

详情 返回 返回

CommonJS模塊分類及加載流程,及模塊加載模擬實現 - 动态 详情

模塊分類

  1. 內置模塊:Node源碼編譯時寫入到二進制文件中
  2. 文件模塊:代碼運行時,動態加載

加載流程

  • 路徑分析:依據標識符確定模塊位置(路徑標識符、非路徑標識符)
  • 文件定位:確定目標模塊中具體的文件及文件類型(存在'm1'模塊,導入時使用require('m1')語法,使用m1.js->m1.json->m1.node的順序,如果都沒找到,會被當做一個目錄,查找package.json文件,使用JOSN.parse()解析。接下來會找main.js->main.json->main.node。將index作為目標模塊中的具體文件名稱)
  • 編譯執行:採用對應的方式完成文件的編譯執行(將某個具體類型的文件按照相應的方式進行編譯和執行,創建新對象,按路徑載入,完成編譯執行,返回可用exports對象)

緩存優先原則

  • 提高模塊加載速度
  • 當前模塊不存在,則經歷一次完整加載流程
  • 模塊加載完成後,使用路徑作為索引進行緩存

模塊加載模擬實現

核心邏輯

  • 路徑分析
  • 緩存優化
  • 文件定位
  • 編譯執行

根據上面的加載流程特徵模擬實現

const fs = require("fs");
const path = require("path");
const vm = require("vm");

function Module(id) {
  this.id = id;
  this.exports = {};
}

// 靜態方法
Module._resolveFilename = function (filename) {
  // 利用path將filename轉為絕對路徑
  let absPath = path.resolve(__dirname, filename);
  console.log(absPath);
  // 判斷當前路徑對應的內容是否存在
  if (fs.existsSync(absPath)) {
    // 條件成立説明absPath對應的內容是存在的
    return absPath;
  } else {
    //   根據文件定位的順序依次去找
    let suffix = Object.keys(Module.__extensions);
    for (var i = 0; i < suffix.length; i++) {
      // 拼接
      let newPath = absPath + suffix[i];
      if (fs.existsSync(newPath)) {
        return newPath;
      }
    }
  }
  throw new Error(`${filename} is not exists`);
};

Module.__extensions = {
  ".js"(module) {
    // 讀取
    let content = fs.readFileSync(module.id, "utf-8");
    // 包裝
    content = Module.wrapper[0] + content + Module.wrapper[1];
    // 使用vm執行
    let compileFn = vm.runInThisContext(content); // 轉換成一個可執行匿名函數
    // 準備參數的值
    let exports = module.exports;
    let dirname = path.dirname(module.id);
    let filename = module.id;
    // 調用
    compileFn.call(exports, exports, myRequire, module, filename, dirname);
  },
  ".json"(module) {
    //   讀取然後格式化
    let content = JSON.parse(fs.readFileSync(module.id, "utf-8"));
    module.exports = content;
  },
};

Module.wrapper = [
  "(function(exports,require,module,__filename,__dirname){",
  "})",
];

Module._cache = {};

Module.prototype.load = function () {
  let extname = path.extname(this.id);
  console.log("extname", extname);
  Module.__extensions[extname](this);
};
function myRequire(filename) {
  // 1,獲取絕對路徑
  let modulePath = Module._resolveFilename(filename);
  //   2,實現緩存優先
  let cacheModule = Module._cache[modulePath];
  if (cacheModule) {
    return cacheModule.exports;
  }
  // 3創建空對象加載目標模塊
  let module = new Module(modulePath);
  // 4,緩存已經加載的模塊
  Module._cache[modulePath] = module;
  // 5,執行加載(編譯執行)
  module.load();
  //  6 ,返回數據
  return module.exports;
}

let obj = myRequire("./m");
console.log(obj);
user avatar Leesz 头像 freeman_tian 头像 huichangkudelingdai 头像 yulong1992 头像 huangmingji 头像 xiao2 头像 ailvyoudetiebanshao 头像 tangzhiyuan 头像 compose_hub 头像 gracetangyi 头像 barrior 头像 duokeli 头像
点赞 23 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.