Node.js 诞生时,JavaScript 还没有官方的模块化标准,于是社区推出了 CommonJS 规范,也就是我们熟悉的 require
和 module.exports
。
后来,JavaScript 官方在 ES6 中推出了 ES Modules(ESM),也就是 import
和 export
。但 Node.js 早期无法直接支持 ESM,直到 13.2 版本才默认支持。于是,两种模块系统“共存”至今。
require
:同步加载模块,代码执行到这一行时,才会去读文件、解析依赖。// 可以动态加载 (比如根据条件判断)
if (user.isVIP) {
const vipModule = require('./vip.js');
}
import
:异步加载模块,代码在编译阶段就确定了依赖关系,路径必须是静态字符串。// 静态路径,不能写在条件语句里!
import vipModule from'./vip.js';
// 动态加载需用 import() 函数 (返回 Promise)
if (user.isVIP) {
const vipModule = await import('./vip.js');
}
总结:require
更灵活,但 import
性能更好 (依赖预加载)。
CommonJS(require):
// 导出
module.exports = { name: '张三' };
// 或
exports.name = '张三';
// 导入
const user = require('./user.js');
console.log(user.name); // 张三
ES Modules(import):
// 导出
export const name = '张三';
// 或默认导出
export default { name: '张三' };
// 导入
import user from'./user.js'; // 默认导出
import { name } from'./user.js'; // 命名导出
注意:ESM 的 export default
对应 CommonJS 的 module.exports
,而 export
对应 exports.xxx
。
require
:导入的是模块的拷贝。如果原模块的值变了,导入的不会跟着变。
// counter.js
let count = 0;
exports.increment = () => count++;
// app.js
const { increment } = require('./counter.js');
increment();
console.log(count); // 报错!count 是 counter.js 内部的变量
import
:导入的是模块的实时引用 (但变量本身是只读的)。
// counter.mjs
export let count = 0;
export const increment = () => count++;
// app.mjs
import { count, increment } from'./counter.mjs';
increment();
console.log(count); // 1(实时更新)
require
:默认支持 .js
和 .cjs
(CommonJS 文件)。import
:需将文件后缀改为 .mjs
,或在 package.json
中设置:{ "type": "module" } // 所有 .js 文件视为 ESM
混用可能导致诡异问题。如果必须混用:
import
,但需确保 CommonJS 模块有默认导出。import()
动态加载 (返回 Promise)。假设你已经安装了 Node.js v16+,跟着以下步骤操作:
mkdir koa-demo&& cd koa-demo
npm init -y
先配置 npm 包安装源,新增 .npmrc
文件,内容如下
registry=https://registry.npmmirror.com
然后执行如下命令
npm install koa --save
在 package.json
中添加:
{ "type": "module" } // 启用 ESM
src/app.js
import Koa from 'koa';
const app = new Koa();
// 中间件:记录请求耗时
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
ctx.set('X-Response-Time', `${duration}ms`);
});
// 路由
app.use(async (ctx) => {
ctx.body = 'Hello, 我是用 import 运行的 Koa 服务!';
});
// 启动服务
app.listen(3000, () => {
console.log('服务已启动:http://localhost:3000');
});
node src/app.js
访问 http://localhost:3000
,你会看到欢迎信息!
require
:传统、灵活,适合老项目。import
:现代、高效,未来趋势。import
,旧项目按需迁移。接下来,我将在 koa-demo 的基础上进行功能开发,新增通过 Markdown 发送邮件的功能。同时,我将使用 ESM 改造我的前端工具包 hui-vue
。你的项目还在使用 require
吗?