Một trong những điều mạnh mẽ nhất về phát triển phần mềm là khả năng tái sử dụng và xây dựng dựa trên nền tảng của người khác. Việc chia sẻ mã này đã giúp phần mềm phát triển với tốc độ đáng kinh ngạc.
Một cơ chế tuyệt vời như vậy rất quan trọng ở cấp độ vi mô đối với cả các dự án và nhóm cá nhân.
Đối với Node.js, quá trình chia sẻ mã này – cả trong các dự án riêng lẻ và trong các phần phụ thuộc npm bên ngoài – được hỗ trợ bằng cách sử dụng module.exports
hoặc exports
.
Làm cách nào để chúng tôi sử dụng tính năng xuất mô-đun để cắm một mô-đun bên ngoài hoặc chia nhỏ dự án của chúng tôi thành nhiều tệp (mô-đun) một cách hợp lý?
Hệ thống mô-đun Node.js được tạo ra bởi vì các nhà thiết kế của nó không muốn nó gặp phải vấn đề tương tự về phạm vi toàn cầu bị hỏng, giống như đối tác trình duyệt của nó. Họ đã triển khai đặc tả CommonJS để đạt được điều này.
Hai phần quan trọng của câu đố là module.exports
và require
chức năng.
Cách thức hoạt động của module.exports
module.exports
thực sự là một tài sản của module
vật. Đây là cách module
đối tượng trông giống như khi chúng ta console.log(module)
:
Module {
id: '.',
path: '/Users/stanleynguyen/Documents/Projects/blog.stanleynguyen.me',
exports: {},
parent: null,
filename: '/Users/stanleynguyen/Documents/Projects/blog.stanleynguyen.me/index.js',
loaded: false,
children: [],
paths: [
'/Users/stanleynguyen/Documents/Projects/blog.stanleynguyen.me/node_modules',
'/Users/stanleynguyen/Documents/Projects/node_modules',
'/Users/stanleynguyen/Documents/node_modules',
'/Users/stanleynguyen/node_modules',
'/Users/node_modules',
'/node_modules'
]
}
Đối tượng trên về cơ bản mô tả một mô-đun được đóng gói từ tệp JS với module.exports
là thành phần được xuất của bất kỳ loại nào – đối tượng, hàm, chuỗi, v.v. Xuất mặc định trong mô-đun Node.js đơn giản như sau:
module.exports = function anExportedFunc() {
return "yup simple as that";
};
Có một cách khác để xuất từ mô-đun Node.js được gọi là “xuất khẩu có tên”. Thay vì gán toàn bộ module.exports
cho một giá trị, chúng tôi sẽ gán các thuộc tính riêng lẻ của giá trị mặc định module.exports
phản đối các giá trị. Một cái gì đó như thế này:
module.exports.anExportedFunc = () => {};
module.exports.anExportedString = "this string is exported";
// or bundled together in an object
module.exports = {
anExportedFunc,
anExportedString,
};
Xuất khẩu được đặt tên cũng có thể được thực hiện chính xác hơn với phạm vi mô-đun exports
biến được xác định trước, như thế này:
exports.anExportedFunc = () => {};
exports.anExportedString = "this string is exported";
Tuy nhiên, việc giao toàn bộ exports
biến thành một giá trị mới sẽ không hoạt động (chúng ta sẽ thảo luận lý do tại sao trong phần sau) và thường gây nhầm lẫn cho các nhà phát triển Node.js.
// This wont work as we would expect
exports = {
anExportedFunc,
anExportedString,
};
Hãy tưởng tượng rằng các bản xuất mô-đun Node.js đang vận chuyển các thùng chứa, với module.exports
và exports
với tư cách là nhân viên cảng, người mà chúng tôi sẽ cho biết “con tàu” (nghĩa là giá trị) nào mà chúng tôi muốn đến “cảng nước ngoài” (một mô-đun khác trong dự án).
Chà, “xuất mặc định” sẽ nói module.exports
“tàu” nào sẽ ra khơi trong khi “xuất khẩu có tên” sẽ chất các công-te-nơ khác nhau lên tàu module.exports
sắp ra khơi.

Bây giờ ta đã cho tàu ra khơi, các “cảng ngoại” của ta quay cuồng đón tàu xuất khẩu như thế nào?
Cách hoạt động của từ khóa yêu cầu Node.js
Ở đầu nhận, các mô-đun Node.js có thể nhập bằng cách require
-ing giá trị xuất khẩu.
Hãy nói rằng điều này đã được viết trong ship.js
:
...
module.exports = {
containerA,
containerB,
};
Chúng tôi có thể dễ dàng nhập “tàu” trong receiving-port.js
:
// importing the whole ship as a single variable
const ship = require("./ship.js");
console.log(ship.containerA);
console.log(ship.containerB);
// or directly importing containers through object destructuring
const { containerA, containerB } = require("./ship.js");
console.log(containerA);
console.log(containerB);
Một điểm quan trọng cần lưu ý về nhà khai thác cảng nước ngoài này – require
– là người đó kiên quyết nhận những con tàu đã gửi bởi module.exports
từ bên kia biển. Điều này dẫn chúng ta đến phần tiếp theo, nơi chúng ta sẽ giải quyết một điểm gây nhầm lẫn phổ biến.
module.exports
so với exports
– Sự khác biệt là gì và bạn sử dụng cái nào khi nào?
Bây giờ chúng ta đã trải qua những kiến thức cơ bản về xuất và yêu cầu mô-đun, đã đến lúc giải quyết một trong những nguồn gây nhầm lẫn phổ biến trong các mô-đun Node.js.
Đây là một lỗi xuất mô-đun phổ biến mà những người mới bắt đầu với Node.js thường mắc phải. Họ chỉ định exports
sang một giá trị mới, nghĩ rằng nó giống như “xuất mặc định” thông qua module.exports
.
Tuy nhiên, điều này sẽ không hoạt động vì:
require
sẽ chỉ sử dụng giá trị từmodule.exports
exports
là một biến trong phạm vi mô-đun đề cập đếnmodule.exports
ban đầu
Vì vậy, bằng cách gán exports
đến một giá trị mới, chúng tôi đang trỏ giá trị của exports
đến một tham chiếu khác từ tham chiếu ban đầu đến cùng một đối tượng như module.exports
.
Nếu bạn muốn tìm hiểu thêm về giải thích kỹ thuật này, tài liệu chính thức của Node.js là một nơi tốt để bắt đầu.
Quay lại phép loại suy mà chúng tôi đã thực hiện trước đây bằng cách sử dụng tàu và người điều khiển: exports
là một nhân viên cảng khác mà chúng tôi có thể thông báo về con tàu sắp đi. Lúc đầu, cả hai module.exports
và exports
có cùng một thông tin về “con tàu” đang đi.
Nhưng nếu chúng ta nói exports
rằng con tàu đi sẽ là một con tàu khác (nghĩa là chỉ định exports
sang một giá trị hoàn toàn mới)? Sau đó, bất cứ điều gì chúng tôi nói với họ sau đó (như gán thuộc tính của exports
đến các giá trị) sẽ không ở trên con tàu đó module.exports
đang thực sự ra khơi để được nhận bởi require
.
Mặt khác, nếu chúng ta chỉ nói exports
để “bốc một số công-te-nơ lên tàu sắp đi” (chỉ định thuộc tính của exports
để định giá), chúng ta thực sự sẽ kết thúc việc chất “công-te-nơ” (nghĩa là giá trị tài sản) lên con tàu đang thực sự ra khơi.
Dựa trên lỗi phổ biến được giải thích ở trên, chúng tôi chắc chắn có thể phát triển một số quy ước tốt xung quanh việc sử dụng các mô-đun CommonJS trong Node.js.
Các phương pháp hay nhất về xuất khẩu Node.js – một chiến lược hợp lý
Tất nhiên quy ước đưa ra dưới đây hoàn toàn là từ những đánh giá và suy luận của riêng tôi. Nếu bạn có một trường hợp mạnh mẽ hơn cho một giải pháp thay thế, xin vui lòng tweet cho tôi @stanley_ngn.
Những điều chính tôi muốn đạt được với quy ước này là:
- loại bỏ sự nhầm lẫn xung quanh
exports
so vớimodule.exports
- dễ đọc và khả năng nhìn cao hơn liên quan đến xuất mô-đun
Vì vậy, tôi đề xuất rằng chúng ta hợp nhất các giá trị đã xuất ở cuối tệp như sau:
// default export
module.exports = function defaultExportedFunction() {};
// named export
module.exports = {
something,
anotherThing,
};
Làm như vậy sẽ loại bỏ bất kỳ nhược điểm nào về tính đồng nhất mà module.exports
có so với tốc ký exports
. Điều này sẽ loại bỏ tất cả các động cơ khuyến khích chúng tôi sử dụng thông tin khó hiểu và có khả năng gây hại exports
.
Phương pháp này cũng sẽ giúp người đọc mã dễ dàng xem qua và tìm hiểu về các giá trị đã xuất từ một mô-đun cụ thể.
Vượt ra ngoài CommonJS
Có một tiêu chuẩn mới và tốt hơn (tất nhiên!) gần đây đã được giới thiệu cho Node.js được gọi là ECMAScript modules
. Trước đây, các mô-đun ECMAScript chỉ có sẵn trong mã mà cuối cùng sẽ cần chuyển mã từ Babel hoặc là một phần của tính năng thử nghiệm trong Node.js phiên bản 12 trở lên.
Đó là một cách khá đơn giản và tao nhã để xử lý xuất mô-đun. Ý chính của nó có thể được tóm tắt với xuất khẩu mặc định là:
export default function exportedFunction() {}
và tên xuất khẩu trông như thế này:
// named exports on separate LOC
export const constantString = "CONSTANT_STRING";
export const constantNumber = 5;
// consolidated named exports
export default {
constantString,
constantNumber,
};
Sau đó, các giá trị này có thể dễ dàng được nhập vào đầu nhận, như sau:
// default exported value
import exportedFunction from "exporting-module.js";
// import named exported values through object destructuring
import { constantString, constantNumber } from "exporting-module.js";
Điều này dẫn đến không còn sự nhầm lẫn từ module.exports
so với exports
và một cú pháp hay, nghe có vẻ giống con người!
Chắc chắn có những dự án chưa được chuyển sang Node.js phiên bản 14 trở lên và do đó không thể sử dụng cú pháp mới này.
Tuy nhiên, nếu bạn có cơ hội (vì bạn đang bắt đầu một dự án mới hoặc dự án của bạn đã được di chuyển thành công sang Node.js 14 trở lên), thì không có lý do gì để không chuyển sang cách làm việc tương lai tuyệt vời này.
Cảm ơn bạn đã đọc!
Cuối cùng nhưng không kém phần quan trọng, nếu bạn thích các bài viết của tôi, vui lòng truy cập blog của tôi để xem các bài bình luận tương tự và theo dõi tôi trên Twitter. 🎉