Node.js 模块系统:CommonJS vs ES Modules

Node.js 模块系统:CommonJS vs ES Modules

理解两种模块系统的区别和使用场景

概述

Node.js 支持两种模块系统:

特性 CommonJS ES Modules
关键字 require / module.exports import / export
加载方式 运行时同步加载 编译时静态加载
文件扩展名 .js .mjs.cjs
Node.js 支持 默认支持 v13.2+ 稳定支持

CommonJS (CJS)

导出

javascript 复制代码
// math.js

// 方式1:导出单个值
module.exports = function add(a, b) {
  return a + b;
};

// 方式2:导出多个属性
module.exports.add = function(a, b) { return a + b; };
module.exports.subtract = function(a, b) { return a - b; };

// 方式3:使用 exports(注意:不能直接赋值)
exports.PI = 3.14159;
exports.multiply = function(a, b) { return a * b; };

导入

javascript 复制代码
// app.js

// 导入整个模块
const math = require('./math');
console.log(math.add(1, 2));

// 解构导入
const { add, PI } = require('./math');
console.log(PI);

// 导入内置模块
const fs = require('fs');
const path = require('path');

// 导入第三方模块
const express = require('express');

package.json 配置

json 复制代码
{
  "type": "commonjs"  // 默认,可省略
}

ES Modules (ESM)

导出

javascript 复制代码
// math.js 或 math.mjs

// 命名导出
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

// 默认导出
export default function(a, b) {
  return a + b;
}

// 或者集中导出
const PI = 3.14159;
const add = (a, b) => a + b;
export { PI, add };

导入

javascript 复制代码
// app.js 或 app.mjs

// 默认导入
import add from './math.js';

// 命名导入
import { add, subtract, PI } from './math.js';

// 重命名导入
import { add as sum } from './math.js';

// 导入所有
import * as math from './math.js';

// 副作用导入(只执行,不导入)
import './polyfill.js';

package.json 配置

json 复制代码
{
  "type": "module"
}

或使用 .mjs 扩展名(自动启用 ESM)


主要区别

1. 加载时机

javascript 复制代码
// CommonJS - 运行时加载
const math = require('./math');  // 同步执行

// ES Modules - 编译时加载
import { add } from './math';    // 静态分析

2. 导出方式

javascript 复制代码
// CommonJS - 值拷贝
let count = 0;
module.exports = { count };
count = 1;  // 导出值不变,仍是 0

// ES Modules - 动态引用
export let count = 0;
count = 1;  // 导出值同步更新为 1

3. this 指向

javascript 复制代码
// CommonJS - this 指向 module.exports
console.log(this === module.exports);  // true

// ES Modules - this 是 undefined
console.log(this);  // undefined

实战示例

CommonJS 项目结构

复制代码
project/
├── package.json (默认 commonjs)
├── utils/
│   └── math.js
└── app.js
javascript 复制代码
// utils/math.js
module.exports.add = (a, b) => a + b;

// app.js
const { add } = require('./utils/math');
console.log(add(1, 2));

ES Modules 项目结构

复制代码
project/
├── package.json ("type": "module")
├── utils/
│   └── math.js
└── app.js
javascript 复制代码
// utils/math.js
export const add = (a, b) => a + b;

// app.js
import { add } from './utils/math.js';
console.log(add(1, 2));

混用技巧

在 CJS 中使用 ESM

javascript 复制代码
// 需要 dynamic import
async function loadModule() {
  const { add } = await import('./math.mjs');
  return add(1, 2);
}

在 ESM 中使用 CJS

javascript 复制代码
// 可以直接导入
import express from 'express';
import { readFile } from 'fs';

选择建议

使用 CommonJS 当:

  • 需要动态加载模块
  • 兼容旧项目或工具
  • 编写简单的脚本工具

使用 ES Modules 当:

  • 开始新项目
  • 使用现代前端框架(React、Vue)
  • 需要_tree-shaking_ 优化
  • 编写可同时在浏览器和 Node.js 运行的代码

快速参考

javascript 复制代码
// CommonJS
module.exports = { value: 1 };
const { value } = require('./file');

// ES Modules
export const value = 1;
import { value } from './file.js';

总结

  • CommonJS:Node.js 传统方式,成熟稳定
  • ES Modules:现代标准,未来趋势
相关推荐
万敏9 小时前
Vue3 全栈实战第五周:Vitest 单元测试从零搭建实战记录
vue.js·node.js·全栈
星栈17 小时前
我以为 TS7.0 只是换个版本号,结果编译快了 9 倍,也踩了 5 个坑
后端·typescript·node.js
Synmbrf17 小时前
Mosquitto 持久化消息积压导致网关内存耗尽的排查
node.js
AINative软件工程19 小时前
LLM Streaming 背压工程实践:慢客户端、断流重连与服务端缓冲区的生产设计
node.js·llm
码林鼠1 天前
webpack的基本配置
前端·webpack·node.js
FeelTouch Labs1 天前
Node.js 中的 spawn 和 exec:子进程执行的不同方式对比
node.js·编辑器·vue·vim
烂蜻蜓2 天前
Node.js入门教程(一):初识Node.js——服务端JavaScript的诞生与特点
开发语言·javascript·node.js
凌云拓界2 天前
NodeVerdict:Node.js 原生诊断数据可视化工具
信息可视化·架构·typescript·开源·node.js·github·bug
用户2930750976692 天前
React Router v6 实战:理解 SPA 路由系统
node.js