最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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.開(kāi)始創(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的proxy-server使用詳解

    node的proxy-server使用詳解

    本文主要介紹了node的proxy-server使用詳解,主要介紹代理服務(wù)器的API基本使用流程和思路,以及代理服務(wù)器實(shí)例的API基本使用流程和思路,感興趣的可以了解一下
    2023-12-12
  • nodejs調(diào)用cmd命令實(shí)現(xiàn)復(fù)制目錄

    nodejs調(diào)用cmd命令實(shí)現(xiàn)復(fù)制目錄

    本文給大家介紹的是如何在nodejs中調(diào)用CMD命令,從而實(shí)現(xiàn)目錄的復(fù)制,非常的實(shí)用,有需要的小伙伴可以參考下。
    2015-05-05
  • 提高Node.js性能的應(yīng)用技巧分享

    提高Node.js性能的應(yīng)用技巧分享

    Node.js 是單線程非阻塞 I/O, 使其可以支持成千上萬(wàn)的并發(fā)操作。這和 NGINX 解決 C10K 問(wèn)題的方式如出一轍。Node.js 以高效的性能和開(kāi)發(fā)效率著稱。
    2017-08-08
  • node.js中的模塊化標(biāo)準(zhǔn)CommonJS與自定義模塊

    node.js中的模塊化標(biāo)準(zhǔn)CommonJS與自定義模塊

    這篇文章介紹了node.js中的模塊化標(biāo)準(zhǔn)CommonJS與自定義模塊,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • Node.js的Koa框架上手及MySQL操作指南

    Node.js的Koa框架上手及MySQL操作指南

    Node世界中新興的Koa框架比Express更加輕便,且擴(kuò)展對(duì)數(shù)據(jù)庫(kù)操作的支持十分強(qiáng)大,這里我們就來(lái)看一下Node.js的Koa框架上手及MySQL操作指南:
    2016-06-06
  • 使用node.JS中的url模塊解析URL信息

    使用node.JS中的url模塊解析URL信息

    本文將詳細(xì)介紹nodeJS中的URL模塊的使用方法,利用URL模塊解析出URL相關(guān)信息
    2020-02-02
  • node.js中的fs.readlink方法使用說(shuō)明

    node.js中的fs.readlink方法使用說(shuō)明

    這篇文章主要介紹了node.js中的fs.readlink方法使用說(shuō)明,本文介紹了fs.readlink方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • Node中fs文件系統(tǒng)模塊的使用方法詳解

    Node中fs文件系統(tǒng)模塊的使用方法詳解

    fs?模塊是?Node.js?官方提供的、用來(lái)操作文件的模塊(內(nèi)置api),它提供了一系列的方法和屬性,用來(lái)滿足用戶對(duì)文件的操作需求,本文給大家介紹了Node中fs文件系統(tǒng)模塊的使用方法,需要的朋友可以參考下
    2024-03-03
  • 詳解如何在vscode里面調(diào)試js和node.js的方法步驟

    詳解如何在vscode里面調(diào)試js和node.js的方法步驟

    這篇文章主要介紹了詳解如何在vscode里面調(diào)試js和node.js的方法步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • Node.JS使用Sequelize操作MySQL的示例代碼

    Node.JS使用Sequelize操作MySQL的示例代碼

    Node.JS提供了操作數(shù)據(jù)庫(kù)的基礎(chǔ)接口,本篇文章主要介紹了Node.JS使用Sequelize操作MySQL的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-10-10

最新評(píng)論

黄骅市| 枣强县| 漯河市| 玉山县| 邯郸市| 吴川市| 香格里拉县| 温泉县| 汽车| 如东县| 南岸区| 北安市| 柳江县| 年辖:市辖区| 句容市| 江阴市| 石家庄市| 鹤壁市| 吉木萨尔县| 甘南县| 榆社县| 曲周县| 贡山| 洞头县| 晋城| 吉林市| 涿州市| 集贤县| 白山市| 太白县| 甘德县| 海晏县| 门源| 科尔| 清远市| 邹平县| 金坛市| 潞西市| 永新县| 且末县| 革吉县|