Node hỗ trợ hai hệ thống module: CommonJS (CJS) — bản gốc, dùng require/module.exports — và ES Modules (ESM) — chuẩn, dùng import/. Chúng khác nhau về cú pháp, cách nạp module và khả năng tương tác.
Node hỗ trợ hai hệ thống module: CommonJS (CJS) — bản gốc, dùng require/module.exports — và ES Modules (ESM) — chuẩn, dùng import/. Chúng khác nhau về cú pháp, cách nạp module và khả năng tương tác.
export// math.js
function add(a, b) { return a + b; }
module.exports = { add };
// app.js
const { add } = require("./math"); // đồng bộ, lúc chạy
// math.mjs (hoặc .js với "type": "module")
export function add(a, b) { return a + b; }
export default something;
// app.mjs
import { add } from "./math.mjs"; // tĩnh, được hoist
CommonJS ES Modules
Cú pháp require/module.exports import/export
Nạp module đồng bộ, lúc chạy tĩnh, được hoist
Top-level await không có
Tree-shaking khó được (phân tích tĩnh được)
Đuôi file .cjs / .js (mặc định) .mjs / .js với "type":"module"
__dirname/__filename có sẵn không có (dùng import.meta.url)
// package.json
{ "type": "module" } // giờ file .js được coi là ESM
Hoặc dùng đuôi .mjs rõ ràng. Nếu không, Node mặc định .js là CommonJS.
// ESM có thể import CommonJS:
import pkg from "some-cjs-lib"; // hoạt động (default import)
// CommonJS KHÔNG THỂ require() một gói thuần ESM (nó bất đồng bộ):
const esm = require("esm-only-pkg"); // ❌ ERR_REQUIRE_ESM
// phải dùng dynamic import: const esm = await import("esm-only-pkg");
Điểm gồ ghề: CJS không thể require() các gói chỉ-ESM — một nỗi đau di chuyển phổ biến. Cũng lưu ý ESM thiếu __dirname (dùng import.meta.url).
Biết cả hai hệ thống là điều thiết yếu cho công việc Node thực tế: ESM là tương lai (chuẩn, tree-shake được, top-level await, đồng nhất với trình duyệt) và là lựa chọn đúng cho dự án mới, nhưng một lượng rất lớn hệ sinh thái npm và code hiện hữu là CommonJS.
Hiểu khác biệt cú pháp, cách bật ESM ("type": "module"), và các giới hạn tương tác (đặc biệt CJS-không-thể-require-ESM) giúp tránh sự nhầm lẫn và lỗi thường gặp khi trộn lẫn chúng.