NodeJS 中的 esModule 与 commonJS
NodeJS 从 v14 版本开始正式支持解析 esModule,通过扩展 .mjs 或 .cjs 后缀,可以将 js 文件解析为 esModule 和 commonJS。
此外,也可以在文件目录下单独设置一个 package.json,声明 { "type": "module },默认在当前目录下使用 esModule 规范,进行模块解析。
那么,Node 当中的 esModule 和 commonJS 有哪些不同呢?
模块加载
在 commonJS 中,require() 的执行是同步的。它在调用后并不会返回一个 Promise 或者回调函数,而是直接从磁盘或者网络中读取资源(可能涉及到磁盘或网络的 I/O),并立即执行脚本中的内容,返回最后将 module.exports 的值。
在 esModule 中,模块的加载是异步的。首先,模块解析器(module loader)会对脚本进行静态解析,无需执行脚本中的内容,就可以分析出所有的 import 和 export 调用。在解析过程中,解析器可以快速地检查脚本引入或导出模块时的语法错误,并及时抛出异常。
esModule 的解析器会异步的下载与解析代码中引入的脚本,递归的分析模块间的引用关系,构建出完成的「模块依赖图谱」(module graph)。当解析完成后,对应的脚本以及它所依赖的脚本,就可以等待执行了。
所有的脚本都是并行下载的,并且按照引入顺序执行。
路径解析
在 esModule 中使用 import 时,必须完成声明文件路径,包括文件扩展名,如 './src/index.js'。
在 node v14 版本中,使用 esModule 必须遵守 file: 协议,即 URL-based paths。
环境变量
commonJS 中的环境变量/对象,包括 require, exports, module.exports, __filename, __dirname,在 esModule 中都是无法使用的。
如果需要在 esModule 使用 require,可以通过 module.createRequire()。
如果需要使用 __filename, __dirname,可以通过 import.meta.url 进行读取。
import { fileURLToPath } from 'url';
import { dirname } from 'path';
console.log(import.meta.url);
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);require.cache
require.cacheesModule 中无法使用 require.cache,而 commonJS 会缓存引入模块的相关信息。每次 require 模块后,commonJS 会缓存对应的模块,从 cache 中删除某个模块,那么下一次 require 会重新加载这个模块。
commonJS 中 require.cache 的数据结构如下:
{
"/esModule-cmd/cmd/env/index.js": {
"id": ".",
"path": "/esModule-cmd/cmd/env",
"exports": {},
"parent": null,
"filename": "/esModule-cmd/cmd/env/index.js",
"loaded": false,
"children": [[Module]],
"paths": [
"/esModule-cmd/cmd/env/node_modules",
"/esModule-cmd/cmd/node_modules",
"/esModule-cmd/node_modules",
"/node_modules"
]
},
"/esModule-cmd/cmd/env/a.js": {
"id": "/esModule-cmd/cmd/env/a.js",
"path": "/esModule-cmd/cmd/env",
"exports": {},
"parent": {
"id": ".",
"path": "/esModule-cmd/cmd/env",
"exports": {},
"parent": null,
"filename": "/esModule-cmd/cmd/env/index.js",
"loaded": false,
"children": [Array],
"paths": [Array]
},
"filename": "/esModule-cmd/cmd/env/a.js",
"loaded": false,
"children": [[Module]],
"paths": [
"/esModule-cmd/cmd/env/node_modules",
"/esModule-cmd/cmd/node_modules",
"/esModule-cmd/node_modules",
"/node_modules"
]
}
}参考
Last updated
Was this helpful?