nodejs 核心 require 的实现

253 阅读1分钟

学习记录 noderequire 实现原理。 主要是通过 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