feng xiaohan

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

  1. 在类 weakmap 中查找是否有缓存,如果没有则通过 fs 读取文件内容并转换为 utf-8 字符串;
  2. 判断是否是以.js结尾,是则读取 package.json 的 type,如果不是 module 则获取父级以及确认报错的行号和错误信息;
  3. 如果都没问题则会调用_compile()进行编译,在这个方法中会将文件名和文件内容进行拼接,将文件内容包装进一个匿名函数中(类似 js 沙箱,隔离上下文环境);
  4. 调用 script 去执行这个匿名函数,并查看里面是否有动态引入的 import,有的话就引入对应模块,最后返回一个执行结果;
  5. 在拼接文件时,会传递五个参数,执行完匿名函数后会有去组装这五个参数(export、require、module、filename、dirname),再使用 ReflectApply 透传给匿名函数,最后返回结果。