学习记录 node 的 require 实现原理。
主要是通过 node 内置的vm模块构建一个虚拟执行沙箱环境,用来解决执行代码,避免污染全局变量。
// require.js
const path = require('path');
const vm = require('vm');
const fs = require('fs');
// 读取绝对路径文件
const pathToFile = path.resolve(__dirname, './index.js');
// 读取文件内容
const content = fs.readFileSync(pathToFile,'utf-8');
const script = new vm.Script(content, {
filename:'index.js'
})
// 在上下文中执行
const result = script.runInThisContext();
执行 node require.js 打印信息
hello world
修改 index.js 内容
//index.js
var a = 123;
console.log('hello world' , (a + 1))
执行 node require.js 打印信息
hello world 124
如果模块中也引入了其他的模块,需要我们实现 requireFn 的效果。思路是包裹一个函数,然后通过闭包传入一些依赖的参数去执行
// require.js
const path = require('path');
const vm = require('vm');
const fs = require('fs');
// 简单通用的require 函数
function requireFn(filename) {
const pathToFile = path.resolve(__dirname, filename);
// 函数内容
const content = fs.readFileSync(pathToFile,'utf-8');
// 闭包包装函数
const wrapper = [
`(function (require, module, exports, __dirname, __filename) {`,
`})`
]
// 生成闭包包装函数
const wrapperContent = wrapper[0] + content + wrapper[1];
// 沙箱环境的代码
const script = new vm.Script(wrapperContent, {
filename:filename
})
// 生成一个函数
const result = script.runInThisContext();
// 定义一个module 导出的内容模板
const module = {
exports: {}
}
// 传入函数执行需要的依赖
result(requireFn, module, module.exports ,__dirname, __filename)
return module.exports;
}
// 全局导出
global.r = requireFn
index.js 改造
// 先引入require方法
require('./require')
const result = r('./module.js');
console.log(result);
module.js 文件
module.exports = 'hello world'
最后执行 node index.js 打印 hello world