欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

nodejs實(shí)現(xiàn)一個(gè)自定義的require方法的詳細(xì)流程

 更新時(shí)間:2025年03月26日 09:09:36   作者:rookiefishs  
大家對(duì)nodejs中的require方法應(yīng)該不會(huì)陌生,這個(gè)方法可以用來(lái)導(dǎo)入nodejs的內(nèi)置模塊,自定義模塊,第三方模塊等,使用頻率非常高,那么這個(gè)方法內(nèi)部是如何實(shí)現(xiàn)的呢?本篇文章就是從頭到尾拆分實(shí)現(xiàn)流程,最終實(shí)現(xiàn)一個(gè)自定義的require方法,需要的朋友可以參考下

1.前言

大家對(duì)nodejs中的require方法應(yīng)該不會(huì)陌生,這個(gè)方法可以用來(lái)導(dǎo)入nodejs的內(nèi)置模塊,自定義模塊,第三方模塊等,使用頻率非常高,那么這個(gè)方法內(nèi)部是如何實(shí)現(xiàn)的呢?本篇文章就是從頭到尾拆分實(shí)現(xiàn)流程,最終實(shí)現(xiàn)一個(gè)自定義的require方法的

2.前置操作

導(dǎo)入所需的nodejs內(nèi)置庫(kù),分別為fs庫(kù)用來(lái)加載文件內(nèi)容,path庫(kù)用于操作路徑,vm模塊執(zhí)行js代碼

const fs = require('fs')
const path = require('path')
const vm = require('vm')

3.操作步驟拆分

  • 路徑分析,解析出絕對(duì)路徑
  • 緩存優(yōu)先原則,加載文件的時(shí)候優(yōu)先從緩存中加載
  • 文件定位,確定當(dāng)前模塊的文件類型(js,json等等文件,方便后續(xù)調(diào)用對(duì)應(yīng)的編譯函數(shù))
  • 編譯執(zhí)行,將加載模塊的內(nèi)容變?yōu)榭梢栽诋?dāng)前模塊中直接使用的數(shù)據(jù)

4.開始創(chuàng)建自定義的require方法

1.創(chuàng)建customRequire方法

function customRequire(moduleName){}

2.創(chuàng)建一個(gè)Module類,用于存儲(chǔ)模塊信息,以及掛載一些輔助方法

class Module {
  constructor(id) {
    this.id = id
    this.exports = {}
  }
}

3.執(zhí)行第一步路徑分析,解析出絕對(duì)路徑操作

// 給module類添加一個(gè)靜態(tài)屬性,用于表示模塊的加載函數(shù)以及模塊后綴的加載順序
Module._extensions = {
  '.js'(module) {},
  '.json'(module) {},
  // ...
}

// 創(chuàng)建輔助方法,用于解析導(dǎo)入模塊的絕對(duì)路徑
Module._resolveFilename = (moduleName) => {
  // 獲取當(dāng)前模塊的絕對(duì)路徑
  let filePath = path.resolve(__dirname, moduleName)
  // 獲取當(dāng)前模塊的后綴名
  const extName = path.extname(moduleName)

  // 判斷文件是否存在
  if (!fs.existsSync(filePath)) {
    // 如果文件不存在,則按照順序查找后綴名
    const keys = Object.keys(Module._extensions)
    // 文件是否存在標(biāo)識(shí)
    let flag = false
    for (let i = 0; i < keys.length; i++) {
      // 獲取當(dāng)前循環(huán)的后綴名
      const currentExtName = keys[i]
      // 拼接后綴名
      const currentFilePath = filePath + currentExtName
      // 判斷拼接后綴名之后的文件路徑是否存在
      if (fs.existsSync(currentFilePath)) {
        // 如果存在,則將當(dāng)前文件路徑賦值給filePath
        filePath = currentFilePath
        // 將文件是否存在標(biāo)識(shí)設(shè)置為true
        flag = true
        // 終止循環(huán)
        break
      }
    }
    // 如果不存在,則進(jìn)行報(bào)錯(cuò)
    if (!flag) {
      throw new Error(`${moduleName} is not exists`)
    }
  }

  // 返回處理后得到的文件的絕對(duì)路徑
  return filePath
}

// 創(chuàng)建自定義的require方法
function customRequire(moduleName) {
  // 路徑分析,解析出模塊的絕對(duì)路徑
  const absPath = Module._resolveFilename(moduleName)
}

4.執(zhí)行緩存優(yōu)先原則,加載文件的時(shí)候優(yōu)先從緩存中加載

// 創(chuàng)建自定義的require方法
function customRequire(moduleName) {
  // 路徑分析,解析出模塊的絕對(duì)路徑
  const absPath = Module._resolveFilename(moduleName)
  // 緩存優(yōu)先原則,加載文件的時(shí)候優(yōu)先從緩存中加載
  const cacheModel = Module._cache[absPath]
  if (cacheModel) return cacheModel.exports
}

5.執(zhí)行創(chuàng)建空對(duì)象目標(biāo)加載模塊操作(使用絕對(duì)路徑作為模塊的id)

// 定義空對(duì)象,用于存儲(chǔ)緩存的模塊
Module._cache = {}

// 創(chuàng)建自定義的require方法
function customRequire(moduleName) {
  // 路徑分析,解析出模塊的絕對(duì)路徑
  const absPath = Module._resolveFilename(moduleName)
  // 緩存優(yōu)先原則,加載文件的時(shí)候優(yōu)先從緩存中加載
  const cacheModel = Module._cache[absPath]
  if (cacheModel) return cacheModel.exports
  
  // 創(chuàng)建空對(duì)象目標(biāo)加載模塊(使用絕對(duì)路徑作為模塊的id)
  let module = new Module(absPath)
  
  // 將已加載的模塊緩存起來(lái)
  Module._cache[absPath] = module
}

6.執(zhí)行模塊的執(zhí)行加載(編譯執(zhí)行)

// 在module類的原型鏈上新增load方法,用于加載模塊
Module.prototype.load = function () {
  // 獲取當(dāng)前模塊的后綴名
  const extName = path.extname(this.id)
  // 根據(jù)后綴名調(diào)用對(duì)應(yīng)的加載函數(shù)
  Module._extensions[extName](this)
}

// 這里定義不同文件的不同加載與處理方法
Module._extensions = {
  '.js'(module) {
    // 讀取module.id對(duì)應(yīng)的文件內(nèi)容(這里的id對(duì)應(yīng)的是需要讀取的文件的絕對(duì)路徑)
    let content = fs.readFileSync(module.id, 'utf8')

    // 包裝內(nèi)容,使其成為一個(gè)函數(shù),主要作用就是為了讓加載的模塊中可以使用一些全局變量
    content = Module.wrapper[0] + content + Module.wrapper[1]

    // 使用VM模塊,將字符串內(nèi)容變?yōu)橐粋€(gè)函數(shù)
    const compileFn = vm.runInThisContext(content)

    // 定義上方創(chuàng)建的函數(shù)的形參對(duì)應(yīng)的實(shí)參
    let exports = module.exports // 這里的module.exports就是接收到的形參,Module的實(shí)例化對(duì)象對(duì)exports靜態(tài)屬性
    let filename = module.id
    let dirname = path.dirname(module.id)

    // 調(diào)用函數(shù),這里使用call方法,
    // 1.將this指向module.exports,call方法的第一個(gè)值就是重新指向的this
    // 2.此時(shí)被加載的模塊中就可以使用module.exports,所以被加載模塊中的變量就可以存儲(chǔ)到module.exports中
    // 3.然后將其他的參數(shù)傳遞給函數(shù)
    // 4.基于call的特性,會(huì)立即執(zhí)行此函數(shù)
    compileFn.call(exports, exports, customRequire, module, filename, dirname)

    // 返回被改動(dòng)后的module.exports
    return module.exports
  },
  '.json'(module) {
    // 讀取module.id對(duì)應(yīng)的文件內(nèi)容(這里的id對(duì)應(yīng)的是需要讀取的文件的絕對(duì)路徑)
    let content = fs.readFileSync(module.id, 'utf8')

    // 將讀取到的內(nèi)容變?yōu)橐粋€(gè)對(duì)象
    content = JSON.parse(content)

    // 將讀取到的內(nèi)容賦值給module.exports
    module.exports = content
  },
  // ...
}

// 定義空對(duì)象,用于存儲(chǔ)緩存的模塊
Module._cache = {}

// 創(chuàng)建自定義的require方法
function customRequire(moduleName) {
  // 路徑分析,解析出模塊的絕對(duì)路徑
  const absPath = Module._resolveFilename(moduleName)
  // 緩存優(yōu)先原則,加載文件的時(shí)候優(yōu)先從緩存中加載
  const cacheModel = Module._cache[absPath]
  if (cacheModel) return cacheModel.exports
  
  // 創(chuàng)建空對(duì)象目標(biāo)加載模塊(使用絕對(duì)路徑作為模塊的id)
  let module = new Module(absPath)
  
  // 將已加載的模塊緩存起來(lái)
  Module._cache[absPath] = module
  
  // 執(zhí)行加載(編譯執(zhí)行)
  module.load()
  
  // 返回模塊加載后的結(jié)果(注意,不能將id也返回出去)
  return module.exports
}

7.測(cè)試自定義require方法的效果如何

// ...上方代碼
// 使用自定義模塊加載js文件
const obj1 = customRequire('./測(cè)試使用文件/測(cè)試使用js')
console.log('接收到使用customRequire模塊導(dǎo)入的js文件中導(dǎo)出的內(nèi)容', obj1)

// 使用自定義模塊加載json文件
const obj2 = customRequire('./測(cè)試使用文件/測(cè)試使用json')
console.log('接收到使用customRequire模塊導(dǎo)入的json文件中導(dǎo)出的內(nèi)容', obj2)

// 重復(fù)加載相同文件內(nèi)容,觀察是否從緩存中讀取
const obj3 = customRequire('./測(cè)試使用文件/測(cè)試使用js')
console.log('接收到使用customRequire模塊導(dǎo)入的js文件中導(dǎo)出的內(nèi)容', obj3)

5.整體代碼

// 導(dǎo)入所需的模塊
const fs = require('fs')
const path = require('path')
const vm = require('vm')

// 創(chuàng)建一個(gè)Module類,用于存儲(chǔ)模塊信息,以及掛載一些輔助方法
class Module {
  constructor(id) {
    this.id = id
    this.exports = {}
  }
}

// 在module類的原型鏈上新增load方法,用于加載模塊
Module.prototype.load = function () {
  // 獲取當(dāng)前模塊的后綴名
  const extName = path.extname(this.id)
  // 根據(jù)后綴名調(diào)用對(duì)應(yīng)的加載函數(shù)
  Module._extensions[extName](this)
}

// 給module類添加一個(gè)靜態(tài)屬性,用于表示模塊的加載函數(shù)以及模塊后綴的加載順序
Module._extensions = {
  '.js'(module) {
    // 讀取module.id對(duì)應(yīng)的文件內(nèi)容(這里的id對(duì)應(yīng)的是需要讀取的文件的絕對(duì)路徑)
    let content = fs.readFileSync(module.id, 'utf8')

    // 包裝內(nèi)容,使其成為一個(gè)函數(shù),主要作用就是為了讓加載的模塊中可以使用一些全局變量
    content = Module.wrapper[0] + content + Module.wrapper[1]

    // 使用VM模塊,將字符串內(nèi)容變?yōu)橐粋€(gè)函數(shù)
    const compileFn = vm.runInThisContext(content)

    // 定義上方創(chuàng)建的函數(shù)的形參對(duì)應(yīng)的實(shí)參
    let exports = module.exports // 這里的module.exports就是接收到的形參,Module的實(shí)例化對(duì)象對(duì)exports靜態(tài)屬性
    let filename = module.id
    let dirname = path.dirname(module.id)

    // 調(diào)用函數(shù),這里使用call方法,
    // 1.將this指向module.exports,call方法的第一個(gè)值就是重新指向的this
    // 2.此時(shí)被加載的模塊中就可以使用module.exports,所以被加載模塊中的變量就可以存儲(chǔ)到module.exports中
    // 3.然后將其他的參數(shù)傳遞給函數(shù)
    // 4.基于call的特性,會(huì)立即執(zhí)行此函數(shù)
    compileFn.call(exports, exports, customRequire, module, filename, dirname)

    // 返回被改動(dòng)后的module.exports
    return module.exports
  },
  '.json'(module) {
    // 讀取module.id對(duì)應(yīng)的文件內(nèi)容(這里的id對(duì)應(yīng)的是需要讀取的文件的絕對(duì)路徑)
    let content = fs.readFileSync(module.id, 'utf8')

    // 將讀取到的內(nèi)容變?yōu)橐粋€(gè)對(duì)象
    content = JSON.parse(content)

    // 將讀取到的內(nèi)容賦值給module.exports
    module.exports = content
  },
  // ...
}

// 定義一個(gè)數(shù)組,用于存儲(chǔ)包裝內(nèi)容
Module.wrapper = [
  "(function(exports, require, module, __filename, __dirname) {\n",
  "\n});"
]

// 創(chuàng)建輔助方法,用于解析導(dǎo)入模塊的絕對(duì)路徑
Module._resolveFilename = (moduleName) => {
  // 獲取當(dāng)前模塊的絕對(duì)路徑
  let filePath = path.resolve(__dirname, moduleName)
  // 獲取當(dāng)前模塊的后綴名
  const extName = path.extname(moduleName)

  // 判斷文件是否存在
  if (!fs.existsSync(filePath)) {
    // 如果文件不存在,則按照順序查找后綴名
    const keys = Object.keys(Module._extensions)
    // 文件是否存在標(biāo)識(shí)
    let flag = false
    for (let i = 0; i < keys.length; i++) {
      // 獲取當(dāng)前循環(huán)的后綴名
      const currentExtName = keys[i]
      // 拼接后綴名
      const currentFilePath = filePath + currentExtName
      // 判斷拼接后綴名之后的文件路徑是否存在
      if (fs.existsSync(currentFilePath)) {
        // 如果存在,則將當(dāng)前文件路徑賦值給filePath
        filePath = currentFilePath
        // 將文件是否存在標(biāo)識(shí)設(shè)置為true
        flag = true
        // 終止循環(huán)
        break
      }
    }
    // 如果不存在,則進(jìn)行報(bào)錯(cuò)
    if (!flag) {
      throw new Error(`${moduleName} is not exists`)
    }
  }

  // 返回處理后得到的文件的絕對(duì)路徑
  return filePath
}

// 定義空對(duì)象,用于存儲(chǔ)緩存的模塊
Module._cache = {}

// 創(chuàng)建自定義的require方法
function customRequire(moduleName) {
  // 1.路徑分析,解析出模塊的絕對(duì)路徑
  const absPath = Module._resolveFilename(moduleName)

  // 2.緩存優(yōu)先原則,加載文件的時(shí)候優(yōu)先從緩存中加載
  const cacheModel = Module._cache[absPath]
  if (cacheModel) return cacheModel.exports

  // 3.創(chuàng)建空對(duì)象目標(biāo)加載模塊(使用絕對(duì)路徑作為模塊的id)
  let module = new Module(absPath)

  // 4.將已加載的模塊緩存起來(lái)
  Module._cache[absPath] = module

  // 5.執(zhí)行加載(編譯執(zhí)行)
  module.load()

  // 6.返回模塊加載后的結(jié)果(注意,不能將id也返回出去)
  return module.exports
}

// 使用自定義模塊加載js文件
const obj1 = customRequire('./測(cè)試使用文件/測(cè)試使用js')
console.log('接收到使用customRequire模塊導(dǎo)入的js文件中導(dǎo)出的內(nèi)容', obj1)

// 使用自定義模塊加載json文件
const obj2 = customRequire('./測(cè)試使用文件/測(cè)試使用json')
console.log('接收到使用customRequire模塊導(dǎo)入的json文件中導(dǎo)出的內(nèi)容', obj2)

// 重復(fù)加載相同文件內(nèi)容,觀察是否從緩存中讀取
const obj3 = customRequire('./測(cè)試使用文件/測(cè)試使用js')
console.log('接收到使用customRequire模塊導(dǎo)入的js文件中導(dǎo)出的內(nèi)容', obj3)

6.總結(jié)

本篇文章解析了require的大致流程(原require肯定不止這么多代碼與功能的,復(fù)雜度要多的多,這里只是大致實(shí)現(xiàn)效果),主要用到了fs,path,vm模塊。

以上就是nodejs實(shí)現(xiàn)一個(gè)自定義的require方法的詳細(xì)流程的詳細(xì)內(nèi)容,更多關(guān)于nodejs自定義require方法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 快速掌握Node.js模塊封裝及使用

    快速掌握Node.js模塊封裝及使用

    這篇文章主要為大家詳細(xì)介紹了Node.js模塊封裝及使用,幫助大家快速掌握Node.js模塊封裝及使用,感興趣的小伙伴們可以參考一下
    2016-03-03
  • 在nodejs中創(chuàng)建child process的方法

    在nodejs中創(chuàng)建child process的方法

    這篇文章主要介紹了在nodejs中創(chuàng)建child process的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • Thinkjs3新手入門之添加一個(gè)新的頁(yè)面

    Thinkjs3新手入門之添加一個(gè)新的頁(yè)面

    Thinkjs 是一個(gè)快速、簡(jiǎn)單的基于MVC和面向?qū)ο蟮妮p量級(jí)Node.js開發(fā)框架,下面這篇文章主要給大家介紹了關(guān)于Thinkjs3新手入門之添加一個(gè)新的頁(yè)面的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2017-12-12
  • sublime text配置node.js調(diào)試(圖文教程)

    sublime text配置node.js調(diào)試(圖文教程)

    下面小編就為大家分享一篇sublime text配置node.js調(diào)試(圖文教程),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • 使用Node操作文件夾的常用API

    使用Node操作文件夾的常用API

    這篇文章我們將學(xué)習(xí)Node對(duì)文件夾的操作,當(dāng)我們學(xué)習(xí)完文件夾的操作后結(jié)合文件的操作我們就可以真正的通過(guò)Node在日常的工作生活中解決許多和文件相關(guān)的問(wèn)題,這篇文章我們將首先講解文件夾操作的幾個(gè)API,然后完成一下最常見(jiàn)的文件夾遞歸的操作,需要的朋友可以參考下
    2024-08-08
  • 深入理解Node.js中CORS的三個(gè)重要響應(yīng)頭

    深入理解Node.js中CORS的三個(gè)重要響應(yīng)頭

    CORS是一種安全機(jī)制,通過(guò)配置適當(dāng)?shù)捻憫?yīng)頭,服務(wù)器可以允許或限制外部域?qū)Y源的訪問(wèn),本文主要介紹了Node.js中CORS的三個(gè)重要響應(yīng)頭,感興趣的可以了解一下
    2024-12-12
  • 在Mac OS下使用Node.js的簡(jiǎn)單教程

    在Mac OS下使用Node.js的簡(jiǎn)單教程

    這篇文章主要介紹了在Mac OS下使用Node.js的簡(jiǎn)單教程,Node.js是讓JavaScript應(yīng)用運(yùn)行于服務(wù)器端的框架,需要的朋友可以參考下
    2015-06-06
  • 使用Node.js給圖片加水印的方法

    使用Node.js給圖片加水印的方法

    使用Node.js給圖片加水印,首先要確保本地安裝了node環(huán)境。然后,我們進(jìn)行圖像編輯操作需要用到一個(gè)Node.js的庫(kù):images。具體詳情大家可以通過(guò)本文了解下
    2016-11-11
  • Node.js復(fù)制文件的方法示例

    Node.js復(fù)制文件的方法示例

    這篇文章主要介紹了Node.js復(fù)制文件的方法,涉及nodejs針對(duì)文件流的創(chuàng)建、讀取、寫入等操作技巧,需要的朋友可以參考下
    2016-12-12
  • node.js安裝超詳細(xì)步驟教程(推薦!)

    node.js安裝超詳細(xì)步驟教程(推薦!)

    其實(shí)Node.js就是運(yùn)行在服務(wù)端的JavaScript,Node.js是一個(gè)基于Chrome?JavaScript運(yùn)行時(shí)建立的一個(gè)平臺(tái),下面這篇文章主要給大家介紹了關(guān)于node.js安裝超詳細(xì)步驟教程的相關(guān)資料,需要的朋友可以參考下
    2023-06-06

最新評(píng)論