Hiểu Về module.exports Trong Node.js: Hướng Dẫn Toàn Diện 🚀
Khi tôi đọc qua một tệp some.config.js trong một dự án, tôi đã gặp dòng lệnh này:
module.exports = { ... };
Ban đầu, nó có vẻ rất bình thường. Nhưng sau đó, tôi tự hỏi:
👉 module.exports thực sự hoạt động như thế nào trong Node.js?
Câu hỏi này đã đưa tôi vào một hành trình khám phá sâu về hệ thống module của Node — từ cách các tệp được bọc lại, cách mà require() hoạt động, và cách mà exports khác với module.exports.
Dưới đây là những gì tôi đã học được (và những gì mỗi lập trình viên JavaScript/Node nên biết) 👇
🔹 module.exports Là Gì Trong Node.js? 💡
Trong Node.js, mỗi tệp được coi là một module.
Khi Node tải một tệp, nó không chạy trực tiếp mà thay vào đó, bọc mã của bạn như sau:
(function (exports, require, module, __filename, __dirname) {
// nội dung tệp của bạn
});
Bộ bọc này cung cấp cho mỗi tệp quyền truy cập vào:
exportsrequiremodule__filename__dirname
Đó là lý do tại sao bạn có thể sử dụng module.exports ở bất kỳ đâu trong tệp mà không cần phải nhập khẩu nó một cách rõ ràng.
🔹 Mối Quan Hệ Giữa exports và module.exports 🔄
Ở đầu mỗi module, Node thực hiện như sau:
exports = module.exports = {};
Điều này có nghĩa là:
exportschỉ là một tham chiếu đếnmodule.exports.- Mặc định, cả hai đều trỏ đến cùng một đối tượng.
- Bất cứ điều gì bạn thêm vào một trong hai (mà không thay đổi) sẽ được xuất ra.
✅ Ví dụ:
exports.a = 10; // hoạt động
module.exports.b = 20; // hoạt động
Kết quả:
{ a: 10, b: 20 }
🔹 Lỗi Thường Gặp ⚠️
Nếu bạn thay đổi giá trị của exports, bạn sẽ phá vỡ liên kết:
// ❌ Sẽ không hoạt động
exports = function () {
console.log("Xin chào");
};
Giờ đây exports trỏ đến một hàm mới, nhưng module.exports vẫn là {}.
Vì vậy, không có gì được xuất ra.
✅ Cách đúng:
module.exports = function () {
console.log("Xin chào");
};
🔹 Cách require() Hoạt Động? 🔍
Khi bạn viết:
const myModule = require("./myModule");
Dưới đây là quy trình hoạt động:
- Node giải quyết đường dẫn tệp (
./myModule.js) - Kiểm tra bộ nhớ cache của module (các module chỉ được tải một lần trong mỗi phiên chạy)
- Bọc và thực thi tệp bên trong chức năng bọc
- Trả về
module.exports
💡 Đó là lý do tại sao require() luôn trả về module.exports — không phải exports.
🔹 Ví Dụ: Sử Dụng module.exports Trong Thực Tế 📝
// user.js
module.exports = {
name: "Yogesh",
greet() {
console.log(`Xin chào, ${this.name}`);
}
};
// app.js
const user = require("./user");
console.log(user.name); // Yogesh
user.greet(); // Xin chào, Yogesh
🔹 CommonJS So Với ES Modules (CJS So Với ESM) ⚡
Node.js hiện đại hỗ trợ cả CommonJS (CJS) và ES Modules (ESM).
✅ CommonJS (mặc định trong Node)
// math.js
module.exports = {
add: (a, b) => a + b
};
// app.js
const math = require("./math");
console.log(math.add(2, 3));
✅ ES Modules (chuẩn JS hiện đại)
// math.mjs
export function add(a, b) {
return a + b;
}
// app.mjs
import { add } from "./math.mjs";
console.log(add(2, 3));
🔑 Sự khác biệt chính:
- Cú pháp →
module.exportsso vớiexport/import - Tải → CommonJS là đồng bộ; ES Modules là bất đồng bộ
- Xuất → CommonJS thường xuất một đối tượng duy nhất; ESM hỗ trợ nhiều xuất tên
- Tương lai → ESM là chuẩn JavaScript chính thức, tốt hơn cho các ứng dụng hiện đại
🔹 Thực Hành Tốt Nhất ✅
- Sử dụng
exports.x = …để thêm nhiều xuất - Sử dụng
module.exports = …khi xuất một thứ chính (hàm, lớp hoặc đối tượng) - Ưu tiên ES Modules (
import/export) trong các dự án mới nếu có hỗ trợ
✨ Kết Luận
Dòng lệnh nhỏ module.exports trong some.config.js đã trở thành một cánh cửa vào nội bộ của Node.js.
Những gì nhìn có vẻ đơn giản —
module.exports = { ... };
— thực sự được hỗ trợ bởi một hệ thống module mạnh mẽ mà:
- Bọc các tệp trong các hàm
- Liên kết
exportsvàmodule.exports - Chỉ trả về
module.exportsthông quarequire() - Hỗ trợ cả CommonJS và ES Modules
Lần tới khi bạn sử dụng module.exports, hãy nhớ rằng:
Nó không chỉ là một mẫu — nó là trái tim của cách Node.js quản lý các module.
✍️ Viết bởi Yogesh Bamanier
🔗 LinkedIn