2023-11-14 17:40:17
ES Modules(ESM)与CommonJS(CJS)在互操作性中的主要陷阱包括默认导出、命名导出、模块导入方式及循环依赖的差异,具体如下:
ESM默认导出需通过default属性访问当ESM模块使用export default导出时,CommonJS通过require引入会得到一个包含default属性的对象。例如:
// math.mjsexport default function add(a, b) { return a + b; }// app.cjsconst add = require('./math.mjs'); // 实际为 { default: [Function: add] }add.default(1, 2); // 正确调用方式直接调用add(1, 2)会报错,需显式访问default属性。
ESM命名导出在require中不可直接使用ESM的命名导出(如export const PI = 3.14)在CommonJS中无法通过require直接访问。例如:
// utils.mjsexport const PI = 3.14;export function square(x) { return x * x; }// app.cjsconst utils = require('./utils.mjs');console.log(utils.PI); // undefinedconsole.log(utils.square); // undefinedCommonJS会得到一个空对象{},因ESM的命名导出未映射到CJS的命名空间。
CommonJS模块被ESM导入时作为default处理ESM通过import导入CommonJS模块时,整个模块对象会成为default导出。例如:
// legacy.jsmodule.exports = { foo: 'bar' };// index.mjsimport stuff from './legacy.js'; // 正确:stuff = { foo: 'bar' }import { foo } from './legacy.js'; // 报错!CommonJS无原生命名导出机制,需通过default导入后解构使用。
循环依赖行为不一致CJS与ESM处理循环依赖的方式不同:
CJS:返回当前执行状态的引用(可能为未初始化的值)。
ESM:使用“实时绑定”(Live Bindings),支持响应式更新。混合使用时,可能因初始化顺序或更新机制差异导致运行时错误,尤其在大型项目中难以排查。
总结建议: