開始之前大家要先熟悉下 node
中常用檔案讀寫,路徑操作等 API
。
實現思路:我們通過定義一個
req
函式,代替node
中的require
,這個函式首先會根據路徑引數進行路徑解析,找到對應檔案;然後判斷快取中是否存在該檔案物件,存在則返回,否則建立該檔案物件,此時不防通過new
一個Module
函式例項來建立;再然後,通過node
中fs
模組的readFileSync
方法載入檔案,並把檔案內容放在閉包函式中,函式引數有exports
、require
、module
,通過node
中vm
模組的runInThisContext
方法建立一個沙箱環境,同時分別給exports
、require
、module
引數賦值並執行,最後快取模組並通過module.exports
返回檔案內容。
一、構造req函式
詳細程式碼如下:
function req (path) {
// 先要根據路徑變成一個絕對路徑
let filename = Module._resolveFilename(path)
// 檔案路徑唯一
if (Module._cache[filename]) {
// 如果載入過 直接把載入過的結果返回
return Module._cache[filename].exports
}
// 通過檔名建立一個模組
let module = new Module(filename)
// 載入模組,根據不同字尾載入不同內容
module.load()
// 進行模組快取
Module._cache[filename] = module
// 返回最後的結果
return module.exports
}
複製程式碼
二、構造Module函式
詳細程式碼如下:
let path = require('path')
let fs = require('fs')
function Module(filename) {
// 預設模組未載入過
this.loaded = false
// 模組的絕對路徑
this.filename = filename
// 模組匯出的結果
this.exports = {}
}
複製程式碼
路徑解析方法如下:
Module._resolveFilename = function (p) {
p = path.join(__dirname, p)
// 如果檔案有字尾
if (!/\.\w+$/.test(p)) {
// 新增副檔名
for (let i = 0; i < Module._extensions.length; i++) {
// 拼出一個路徑
let filePath = p + Module._extensions[i]
try {
// 判斷檔案是否存在
fs.accessSync(filePath)
// 返回檔案路徑
return filePath
} catch (e) {
// 如果accessSync方法報錯,說明檔案不存在,則丟擲檔案未找到異常
if (i > Module._extensions.length) {
throw new Error('module not found')
}
}
}
} else {
// 否則直接返回檔案路徑
return p
}
}
複製程式碼
Module
函式的主要功能即是 load
方法,如果目標檔案是 js
檔案,則按照 js
方式載入,如果目標檔案是 json
檔案,則按照 json
方式載入。詳細程式碼如下:
let vm = require('vm')
// 模組載入函式
Module.prototype.load = function () {
// 取到檔名稱字尾
let extname = path.extname(this.filename)
// 根據不同字尾讀取檔案內容
Module._extensions[extname](this)
// 標記模組已載入
this.loaded = true
}
// 模組快取物件
Module._cache = {}
// 檔案字尾
Module._extensions = ['.js', '.json']
// json格式檔案讀取方式
Module._extensions['.json'] = function (module) {
let content = fs.readFileSync(module.filename, 'utf8')
module.exports = JSON.parse(content)
}
// 建立執行js沙箱環境時需要
Module.wrapper = ['(function (exports, require, module){', '\n})']
// js程式碼拼接
Module.wrap = function (content) {
return Module.wrapper[0] + content + Module.wrapper[1]
}
// js格式檔案讀取方式
Module._extensions['.js'] = function (module) {
// 讀取檔案
let content = fs.readFileSync(module.filename, 'utf8')
// 把檔案內容放到閉包函式中
let script = Module.wrap(content)
// 建立不影響外界上下文的沙箱環境
let fn = vm.runInThisContext(script)
// 讓閉包函式執行,同時給函式中export require module變數賦值
fn.call(module.exports, module.exports, req, module)
}
複製程式碼
好了,實現起來是不是很簡單,下面測試一下:
// 在同級目錄下建立test.js
// module.exports = 'Hello World'
let str = req('./test')
console.log(str)
複製程式碼
好了,到這裡全部程式碼已經給出,就到此為止吧~ ???