Nodejs 模块化
Nodejs 模块化遵循两套规范:CommonJS 和 ESM 规范。
CommonJS
使用require()引入模块,一共五种类型引入模块:自定义、第三方、内置、C++扩展、JSON 文件
使用module.exports导出,支持解构。
ESM
使用import引入,export导出。
注意: esm 不支持引入 JSON 文件。但是 node16 以上可以使用断言来引入:
import json from "./test.json" assert { type: "json" };
CommonJS 和 ESM 区别
- CJS 是基于运行时同步加载,ESM 是基于编译时异步加载;
一般
import语句只能在顶层,而require()可以在块级作用域底下;但是可以使用import()的函数模式,返回一个 promise。 - CJS 可以修改值,ESM 只读;
- CJS 不支持 tree shaking,ESM 支持;
- CJS 中顶层的 this 指向模块本身,而 ES6 中顶层 this 指向 undefined;
原理
各个文件引入并使用的原理。
.json
使用 fs 直接读取文件内容并转换为 utf-8 字符串,然后再将其转换为对象。
.node
使用 process 读取文件内容。
.js
- 在类 weakmap 中查找是否有缓存,如果没有则通过 fs 读取文件内容并转换为 utf-8 字符串;
- 判断是否是以
.js结尾,是则读取 package.json 的 type,如果不是 module 则获取父级以及确认报错的行号和错误信息; - 如果都没问题则会调用
_compile()进行编译,在这个方法中会将文件名和文件内容进行拼接,将文件内容包装进一个匿名函数中(类似 js 沙箱,隔离上下文环境); - 调用 script 去执行这个匿名函数,并查看里面是否有动态引入的 import,有的话就引入对应模块,最后返回一个执行结果;
- 在拼接文件时,会传递五个参数,执行完匿名函数后会有去组装这五个参数(export、require、module、filename、dirname),再使用 ReflectApply 透传给匿名函数,最后返回结果。