require VS import

RequireJS用得很多了, 但是CommonJS和ES6的模块还没有系统地学习过, 经常看到各式各样的requireimport, 有些晕, 特此总结.

这里对比的是:

  1. 来自CommonJS的require
  2. 来自ESM (ECMAScript Modules)的import

语法

CommonJS require

// dep.js
dep = {
    foo: function(){},
    bar: 22
}
module.exports = dep;

// app.js
var dep = require("dep");
console.log(dep.bar);
dep.foo();

ESM import

// dep.js
export foo function(){};
export const bar = 22;

// app.js
import {foo, bar} from "dep";
console.log(bar);
foo();

依赖解析

CommonJS require的依赖解析

Node.js使用的是CommonJS模式. 它将require的代码用函数包裹起来, 如下:

function (exports, require, module, __filename, __dirname) {
  const m = 1;
  module.exports.m = m;
}

可以看到exports, require, module都是局部变量, 你代码中的require()module.exports其实都是使用了这些局部变量.

这里有个小问题, 那exportsmodule.exports有什么区别?
参考我的另一篇文章CommonJS中exports和module.exports的区别

require加载分为5步:

  1. 解析 (Resolution): node内部解析文件的路径
  2. 加载 (Loading): 从对应路径加载文件
  3. 包裹 (Wrapping): 使用上面的代码包裹拉取来的文件
  4. 运算 (Evaluation): 由VM运算文件中的内容
  5. 缓存 (Caching)

因此, 在Evaluation之前, node是无法知道一个CommonJS的Module里面导出了那些符号的, 这是其与ESM最大的不同.

ESM import的依赖解析

ESM是语法层面(Lexical)上的实现, 在代码被Evaluate之前就可以知道那些变量被导出了. 当ESM模块被词法解析时, 交给VM运算之前, 它会内部构建一个Module Record结构, 其中记录了一个导出变量的列表. 对于import {f} from "foo", 它自动建立了一个本地局部的ffoo模块内部的f之间的映射.

因此, 导入导出变量中的不匹配, 在代码运行之前就会被发现并报错.

总结

require import
来源 CommonJS ESM (ECMAScript Modules)
实现手段 函数包裹 语法层面
导出符号时间 运行时 解析时
报错时间 运行时 解析时

参考

  1. import vs require – ESM & commonJs module differences

你可能感兴趣的:(require VS import)