Node.js 中 require 与 import/export 的区别

require 是 CommonJS 动态加载(运行时同步求值),import/export 是 ES 模块静态声明(编译期分析、支持 tree-shaking)。Node 12+ 原生支持 ESM:package.json 设 "type":"module" 或用 .mjs 后缀。新项目推荐 ESM。

最佳实践
Node.js 事件循环与异步任务示意

require/module.exports 属于 CommonJS(Node 的传统模块系统),import/export 属于 ECMAScript 标准模块(ESM)。 核心区别:require 是运行时的函数调用、可写在任何位置、同步加载;import 是静态声明、必须在顶层、支持静态分析和 tree-shaking。Node 12+ 已原生支持 ESM,新项目一律用 ESM。

语法对照

// CommonJS
const fs = require('fs');
module.exports = myModule;

// ESM
import fs from 'node:fs';
export default myModule;
export const helper = () => {};

关键差异

维度 CommonJS (require) ESM (import)
加载时机 运行时动态 编译期静态
可否条件加载 可以(if 里 require) 静态不行,用动态 import()
加载方式 同步 异步
tree-shaking 不支持 支持
浏览器原生 不支持 支持
顶层 await 不支持 支持

如何启用 ESM

// package.json
{ "type": "module" }

或者文件直接用 .mjs 后缀;CommonJS 文件对应 .cjs。

互操作

  • ESM 里可以 import cjs from './old.cjs'(拿到的是 module.exports 作为 default);
  • CJS 里不能 require() ESM 模块(Node 22+ 的部分版本开始实验性支持 require(esm)),通用做法是用动态 const m = await import('./m.mjs')。

常见问题(FAQ)

Q:老项目要迁移吗?

不必强迁。大量生产代码仍是 CJS 且运行良好。新包、新服务用 ESM;库作者可考虑双格式发布(bundler 会用 ESM 入口做 tree-shaking)。

Q:ESM 里 __dirname 没了怎么办?

用 import.meta.url 换算:const __dirname = path.dirname(fileURLToPath(import.meta.url));,Node 20.11+ 可直接 import.meta.dirname。

Q:为什么 ESM 里引入本地文件必须写全后缀?

ESM 遵循浏览器规则,路径必须完整:import './util.js' 而不是 ./util。CommonJS 的自动补全在 ESM 中不适用。

参考资料

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台