手写commonJS里面的require函数

const { resolve, extname } = require("path");
const { readFileSync, accessSync } = require("fs");
const { runInThisContext } = require("vm");  //这个可以把字符串函数转换成真正的函数
class Module {
  //声明一个Module类,用于模块的数据
  static _extname = [".js", ".json", ".node"];
  static _cache = new Map(); //用于缓存加载过的模块
  static _extension = {
    //用于处理不同后缀的文件
    ".js"(module) {
      //   开始读取文件
      let fileContext = readFileSync(module.id, "utf8");
      // 读取文件后,要执行这串内容
      const fun = runInThisContext(`
  (function(exports,require,module,filename,dirname){
    ${fileContext}
  })
  `);
      fun.call(
        module.exports,
        module.exports,
        require,
        module,
        __filename,
        __dirname
      );
    },
  };
  exports = {};
  constructor(id) {
    this.id = id;
  }
}
module.exports = function (path) {
  let index = 0;
  let absPath = resolve(__dirname, path); //拿到绝对路径
  let orginPath = absPath; //存储一份原始的绝对路径
  const realFilePath = (absPath) => {
    if (index > Module._extension.length) {
      throw Error("请输入正确的文件路径!!!");
    }
    try {
      // 通过accessSync来判断当前路径的文件是否存在,如果存在将其返回,不存在会抛异常,走catch,递归查找
      accessSync(absPath);
      return absPath;
    } catch {
      return realFilePath(orginPath + Module._extname[index++]);
    }
  };
  const handleModule = (module) => {
    //处理模块
    let path = module.id; //拿到模块的绝对路径
    let ext = extname(path); //拿到模块的后缀
    Module._extension[ext](module);
  };
  // 传进来的可能只是./a,没有文件后缀,所以需要自行匹配,拿到正确的路径
  let realAbsPath = realFilePath(absPath);
  // 判断缓存里面是否有,有就直接返回模块的exports
  if (Module._cache.has(realAbsPath)) {
    return Module._cache.get(realAbsPath).exports;
  }
  let module = new Module(realAbsPath);
  handleModule(module);
  Module._cache.set(realAbsPath, module);
  return module.exports;
};

测试方式:新建一个b.js和a.js文件,在b.js中通过require来引入手写的myRequire函数,然后再通过myRequire来引入a.js

let myRequire = require("./myRequire.js");
let teacher = myRequire("./a");
console.log(teacher)

然后再附一道面试题

this.a = 1
exports.b = 2
exports = {c:3}
module.exports = {d:4}
exports.e = 5
module.exports.g = 7
this.f = 6

解析:
通过下面代码可以知道this === exports === module.exports,因为在执行fun.call的时候,传入的都是一个东西,
1.第一行 this = {a:1}
2.第二行 this = {a:1,b:2}
3.第三行因为重新赋值了,所以不在指向this这个对象了,所以 exports = {c:3}
4.第四行也重新赋值了,所以也不在指向this这个对象,所以module.exports = {d:4}
5.第五行 exports = {c:3,e:5}
6.第六行 module.exports = {d:4,g:7}
7.第七行 this = {a:1,b:2,f:6}
因为最终返回的是module.exports,所以结果是{d:4,g:7}

  static _extension = {
    //用于处理不同后缀的文件
    ".js"(module) {
      //   开始读取文件
      let fileContext = readFileSync(module.id, "utf8");
      // 读取文件后,要执行这串内容
      const fun = runInThisContext(`
  (function(exports,require,module,filename,dirname){
    ${fileContext}
  })
  `);
      fun.call(
        module.exports,
        module.exports,
        require,
        module,
        __filename,
        __dirname
      );
    },
  };
  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`js.commonjs-require-cwd` 参数用于指定 CommonJS 模块的当前工作目录。默认情况下,该参数设置为 `false`,表示使用 Node.js 的默认行为。如果需要自定义当前工作目录,可以将该参数设置为一个字符串,表示当前工作目录的绝对路径。 例如,如果你的项目文件结构如下: ``` my-project/ ├── node_modules/ ├── src/ │ ├── index.js │ └── utils/ │ └── helper.js └── package.json ``` 其中,`index.js` 文件中使用了 `helper.js` 模块: ```js const helper = require('./utils/helper'); ``` 如果你想要在 Webpack 中模拟 Node.js 的 `__dirname` 行为,可以将 `js.commonjs-require-cwd` 参数设置为 `__dirname`,如下所示: ```js module.exports = { // ... module: { rules: [ { test: /\.js$/, loader: 'babel-loader', options: { presets: ['@babel/preset-env'], }, }, ], }, resolve: { fallback: { path: require.resolve('path-browserify'), }, }, experiments: { asyncWebAssembly: true, }, output: { globalObject: 'this', }, // 设置 js.commonjs-require-cwd 参数 // 表示使用当前文件所在的目录作为 CommonJS 模块的当前工作目录 // 这样可以模拟 Node.js 的 __dirname 行为 experiments: { asyncWebAssembly: true, }, output: { globalObject: 'this', }, // 设置 js.commonjs-require-cwd 参数 // 表示使用当前文件所在的目录作为 CommonJS 模块的当前工作目录 // 这样可以模拟 Node.js 的 __dirname 行为 node: { __dirname: true, }, // ... }; ``` 这样,在 Webpack 中编译 `index.js` 文件时,`helper.js` 模块的路径就会根据 `index.js` 文件所在的目录来解析。如果 `js.commonjs-require-cwd` 参数设置为 `false`,则 `helper.js` 模块的路径会根据 Webpack 的当前工作目录来解析,可能会导致路径不正确的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值