CommonJS模块导入机制

CommonJS模块导入机制

在CommonJS中我们通过require方法导入模块,通过module.exports导出模块,module.exports默认的出口。我们也可以通过exports.属性(跟this.属性是同样效果)导出一个变量。之所以能这样使用,是因为模块中exports和this对象都与模块(module)的exports属性指向同一块内存地址。
如果把exports或者this赋值为一个变量,那么exports和this将不再指向module.exports引用的内存地址,那么通过那么这种方式不会起作用。
exports和module.exports也不要混合使用。

require导入模块文件的过程:
1. 将导入的模块解析为绝对路径,然后尝试判断这个模块是否存在,存在就继续执行,如果不存在就尝试为其添加扩展名(.js、.json),并再次判断模块是否存在,存在就继续执行,直到报错;
2. 从缓存中寻找模块,找到就返回;
3. 如果缓存中没有找到就通过new Module创建一个实例,并缓存这个模块;
4. 调用模块原型方法load加载这个模块;
5. 从模块id(也就是文件路径)中寻找extname,并通过策略模式加载相应的模块;
6. 如果这个文件是js文件,就读取文件的内容,并为其添加一个函数包裹,然后通过vm.runInThisContext方法将其转化为函数,并且传入参数然后调用这个函数;
7. 如果这个文件是json文件,就读取文件的内容并转化为JSON对象赋值给module.exports属性;
8. 返回module.exports属性。

// require.js
const fs = require('fs')
const path = require('path')
const vm = require('vm')

function Module (id) {
  this.id = id
  this.exports = {}
}

Module._extensions = {
  '.js' (module) {
    const filename = module.id
    // 同步读取script文件
    let script = fs.readFileSync(filename, 'utf8')
    // 为script添加包裹函数
    script = `(function (exports, module, require, __dirname, __filename) { ${script }})`
    // 将script字符串转化为真正的函数
    const fn = vm.runInThisContext(script)
    const dirname = path.dirname(filename)
    const exports = module.exports
    const thisValue = exports
    // 执行函数,并传入参数
    fn.call(thisValue, exports, module, req, dirname, filename)
  },
  '.json' (module) {
    // 同步读取JSON文件并将结果赋值给module.exports
    const content = fs.readFileSync(module.id, 'utf8')
    module.exports = JSON.parse(content)
  }
}

Module._resolveFilename = function (filename) {
  const resolvePath = path.resolve(__dirname, filename) // 将文件名转化为绝对路径
  const isExists = fs.existsSync(resolvePath)
  if (isExists) return resolvePath
  // 然后尝试添加后缀定位文件
  for (const ext of Object.keys(Module._extensions)) {
    const newResolvePath = resolvePath + ext
    const isExists = fs.existsSync(newResolvePath)
    if (isExists) return newResolvePath
  }
  throw new Error(`cannot resolve module from ${filename}`)
}

Module._cache = {}

Module.prototype.load = function () {
  const filename = this.id
  // 通过策略模式去加载不同的模块
  const extname = path.extname(filename)
  Module._extensions[extname](this)
}

function req (filename) {
  // 将文件名转化为绝对路径,然后尝试添加后缀定位文件
  filename = Module._resolveFilename(filename)
  // 确定文件路径,先从缓存中去取文件
  const cachedModule = Module._cache[filename]
  if (cachedModule) return cachedModule.exports
  // 创建模块,并缓存模块
  const module = new Module(filename)
  Module._cache[filename] = module
  // 加载文件
  module.load()
  // 返回文件的导出内容
  return module.exports
}

const a = req('./a')
console.log(a)

// a.js
const a = 'hello a'
module.exports = a
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值