前言:可以用VS code的断点调试一步一步走进去看,其实真整个过程并不复杂。看完之后先理一下思路,然后自己写一遍。写的时候肯定会遇到具体的问题,再到源码中看它是怎么解决的。就可以啦~
思路:每次在文件内调用require('path')的时候,会走到静态方法Module_load上,然后生成新的module实例。接着加载文件,执行文件。
核心是Module类,,父类上有一个核心方法
Module._load // 用于new新的Module实例,缓存控制,最后调用私有方法tryModuleLoad
私有核心方法
tryModuleLoad // 会去调用实例.load加载文件
原型链核心方法
Module.prototype.load // 加载文件,调用_compile
Module.prototype.require // 调用父类Module._load
Module.prototype._compile // 执行加载到的文件
自己实现的整体思路。包括缓存和自动添加拓展名。
const fs = require('fs')
const vm = require('vm')
const path = require('path')
function getFilePath (filePath) {
// 前面还需要有一个是否绝对路径的判断
let absolutePath = path.resolve(__dirname, filePath)
let extname = path.extname(absolutePath)
let realPath = ''
if (extname) {
try {
fs.accessSync(absolutePath)
realPath = absolutePath
} catch (e) {}
} else {
for (let i = 0, len = Module._extnames.length; i < len; ++i) {
try {
let joinedPath = absolutePath + Module._extnames[i]
console.log(joinedPath)
fs.accessSync(joinedPath)
realPath = joinedPath
break
} catch (e) {}
}
}
if (!realPath) {
var err = new Error(`Cannot find module '${filePath}'`)
err.code = 'MODULE_NOT_FOUND'
throw err
}
return realPath
}
function makeRequireFunction (mod) {
function require (path) {
return mod.require(path)
}
return require
}
function join (script) {
return Module.makeUpFunctions[0] + script + Module.makeUpFunctions[1]
}
class Module {
constructor (id) {
this.id = id
this.exports = {}
}
require (id) {
return Module._load(id)
}
load (filePath) {
const script = fs.readFileSync(filePath, 'utf-8')
const code = vm.runInThisContext(join(script))
this._compile(code)
}
_compile (code) {
const require = makeRequireFunction(this)
const result = code.call(this.exports, this.exports, require, this, __dirname, __filename)
// 这个返回值在源码中没有用到。
}
}
Module.makeUpFunctions = [
'(function (exports, require, module, __dirname, __filename) {\n',
'\n})'
]
Module._cache = []
Module._extnames = ['.js', '.json', '.node']
Module._load = function (id) {
const module = new Module(id)
const filePath = getFilePath(id)
if (Module._cache[filePath]) {
return Module._cache[filePath].exports
}
Module._cache[filePath] = module
module.load(filePath)
return module.exports
}
function require2 (id) {
return Module._load(id)
}
const test = require2('./test')
const test2 = require2('./test')
console.log(test)
console.log(test2)
注意:require第二遍的时候,不会再去执行一遍该模块里面的代码了,直接把该模块导出。