Code của bạn đang rối tung lên như một mớ bòng bong, biến toàn cục thì loạn xạ đè lên nhau? Mình cũng từng vật lộn với nó trong những dự án đầu tay, khi file script dài hàng ngàn dòng không thể kiểm soát. May sao, khái niệm Module ES6 là gì? xuất hiện như một vị cứu tinh cho giới lập trình viên frontend. Nắm vững JavaScript Module import export hướng dẫn qua hai khái niệm cốt lõi: export default cho “món chính” và export thường (named export) cho “các món phụ”, là bạn đã giải quyết được 80% vấn đề cấu trúc code rồi đó. Nếu bạn đang loay hoay xây dựng nền tảng từ con số không, việc tham khảo Học JavaScript cơ bản cho người mới 2026 sẽ là bước đệm tuyệt vời trước khi đi sâu vào module. Cùng Phạm Hải mổ xẻ chi tiết cách áp dụng ngay nhé!
Named export và Default export: Khi nào dùng cái nào, đây là câu trả lời!
Named export và default export khác nhau như thế nào? Hiểu một cách đơn giản nhất, default export dùng để xuất ra một giá trị duy nhất mang tính đại diện cho cả file, trong khi named export cho phép bạn xuất nhiều biến, hàm hoặc class độc lập từ cùng một file đó.
Default Export: “Vedette” của mỗi file module
Mỗi file module chỉ được phép có duy nhất một default export. Đây thường là thành phần quan trọng nhất, “ngôi sao” của file đó, ví dụ như một class chính xử lý logic hoặc một component giao diện trong React/Vue. Khi tìm hiểu cách sử dụng import export trong JavaScript?, bạn sẽ nhận ra sức mạnh của default export nằm ở chỗ nó cho phép ta đặt tên tùy ý khi import ở một file khác.
Điều này mang lại sự linh hoạt tuyệt vời khi tổ chức mã nguồn. Ví dụ, bạn có file User.js, bạn chỉ cần viết export default class User {}. Sang một file khác, bạn hoàn toàn có thể gọi nó bằng một cái tên thuần Việt: import NguoiDung from './User.js' mà trình duyệt không hề báo lỗi. Tính năng này giúp code dễ đọc hơn khi ngữ cảnh thay đổi.
Named Export: Khi bạn cần export cả một “rổ” công cụ
Trái ngược với default, named exports không bị giới hạn số lượng trong một file. Đây chính là đáp án hoàn hảo cho câu hỏi cách export nhiều giá trị trong JavaScript module?. Kỹ thuật này cực kỳ hiệu quả khi bạn có một file chứa hàng loạt các hàm tiện ích (utilities) chuyên dùng để tính toán hoặc format dữ liệu. Vì module là một phần của tiêu chuẩn ECMAScript 2015, bạn có thể xem thêm bài viết ES6 JavaScript tính năng mới cần biết để hiểu rõ hơn về hệ sinh thái mạnh mẽ này.
Khi thực hiện import các named exports, bạn bắt buộc phải sử dụng dấu ngoặc nhọn {} và gọi chính xác tên biến đã được export trước đó. Ví dụ: bạn có export const sum = (a, b) => a + b; và export const pi = 3.14;. Ở file đích, bạn sẽ phải viết import { sum, pi } from './math.js'. Sự chặt chẽ này giúp trình duyệt và các công cụ đóng gói biết chính xác bạn đang cần dùng thành phần nào.
Bảng so sánh nhanh: Đặt lên bàn cân cho dễ chọn
Để dễ hình dung và ghi nhớ lâu hơn, tại Phạm Hải, chúng mình thường tóm tắt sự khác biệt cốt lõi qua bảng so sánh dưới đây. Bạn có thể lưu lại để tra cứu nhanh khi code:
| Tiêu chí | Default Export | Named Export |
|---|---|---|
| Số lượng cho phép | Chỉ 1 duy nhất mỗi file | Không giới hạn |
| Cú pháp Import | Không cần cặp ngoặc {} |
Bắt buộc nằm trong {} |
| Quy tắc đặt tên | Tùy ý thay đổi khi import | Phải đúng tên gốc (trừ khi dùng as) |
Các “chiêu thức” import và export nâng cao bạn cần biết
Để làm chủ hoàn toàn các module và viết code như một senior, bạn cần nắm bắt các kỹ thuật nâng cao như đổi tên linh hoạt bằng as, gom nhóm dữ liệu với ký tự *, hoặc cấu trúc lại toàn bộ thư mục bằng kỹ thuật re-export chuyên nghiệp.
Đổi tên “vũ khí” với từ khóa ‘as’ cho đỡ bị trùng
Trong quá trình làm việc thực tế với nhiều thư viện khác nhau, việc trùng lặp tên hàm (ví dụ: cùng có hàm formatDate) là chuyện thường ngày ở huyện. Kỹ thuật đổi tên import export trong JavaScript bằng từ khóa as chính là chiếc phao cứu sinh của bạn. Nó cho phép bạn “thay tên đổi họ” cho module ngay tại thời điểm import để tránh xung đột hệ thống.
Cú pháp rất trực quan: import { fetchData as fetchUserData } from './userApi.js';. Giờ đây, hàm fetchData gốc đã mang một cái tên mới là fetchUserData và hoạt động an toàn trong file hiện tại. Bạn cũng có thể áp dụng cú pháp này ngay lúc export: export { myFunction as newFunctionName };.
Gom tất cả vào một rổ với ‘import * as’ – Con dao hai lưỡi
Rất nhiều bạn newbie thường thắc mắc Import * as trong JavaScript là gì?. Đây là một cú pháp đặc biệt cho phép bạn gom tất cả các named exports của một file cụ thể vào một object (đối tượng) duy nhất. Ví dụ: thay vì import lẻ tẻ, bạn viết import * as MathUtils from './math.js'. Sau đó, bạn có thể gọi các hàm bên trong bằng cách dùng dấu chấm: MathUtils.sum(1, 2).
Tuy nhiên, với kinh nghiệm tối ưu hiệu suất dự án, mình khuyên bạn nên cẩn thận với “chiêu” này. Việc import toàn bộ một file lớn có thể làm tăng dung lượng bundle cuối cùng, gây cản trở quá trình tối ưu hóa mã (đặc biệt là kỹ thuật tree-shaking loại bỏ code thừa) của các công cụ như Webpack hay Vite. Chỉ dùng nó khi bạn thực sự cần dùng đến 80-90% các hàm trong file đó.
Re-export: “Nhà phân phối” module chuyên nghiệp
Khi dự án phình to ra với hàng chục, hàng trăm file, việc chia nhỏ file là điều bắt buộc để tồn tại. Kỹ thuật Re-export giúp bạn tạo ra một file index.js duy nhất làm cổng giao tiếp (entry point) cho toàn bộ một thư mục. Điều này giúp việc tổ chức mã trở nên cực kỳ gọn gàng, che giấu đi sự phức tạp của cấu trúc thư mục bên trong.
Thay vì bắt người dùng import lẻ tẻ từ nhiều file nhỏ, bạn gom chúng lại tại index.js. Trong lập trình hiện đại, các module thường chứa rất nhiều logic gọi API. Nếu bạn chưa tự tin về cách xử lý bất đồng bộ, hãy đọc qua Async Await Promise JavaScript dễ hiểu để nắm vững nền tảng trước khi đóng gói chúng thành các module phân phối. Cú pháp re-export rút gọn thường dùng là: export { default as UserCard } from './UserCard.js'; hoặc export * from './utilities.js';.
Thực hành: Tích hợp JavaScript module vào dự án HTML thực tế

Để chạy trực tiếp module ES6 trên môi trường trình duyệt, bạn bắt buộc phải khai báo thuộc tính type="module" trong thẻ script và chạy ứng dụng thông qua một local server ảo để tránh các lỗi bảo mật nghiêm ngặt của trình duyệt.
Kích hoạt “chế độ module” trong HTML với type="module"
Nhiều bạn thắc mắc Sử dụng module JavaScript trong HTML như thế nào? cho đúng chuẩn. Rất đơn giản, bạn chỉ cần thêm thuộc tính type=”module” vào thẻ <script> khi gọi file .js gốc của bạn (thường là main.js hoặc app.js). Trình duyệt hiện đại sẽ tự động nhận diện và xử lý file đó như một module độc lập, kích hoạt chế độ Strict Mode mặc định.
Ví dụ cụ thể: <script type="module" src="main.js"></script>. Từ file main.js này, bạn có thể thoải mái import các file khác và bắt đầu thao tác với giao diện. Để làm chủ việc biến đổi giao diện người dùng linh hoạt từ bên trong các module, kỹ năng DOM JavaScript thao tác phần tử HTML là hành trang không thể thiếu đối với mọi lập trình viên frontend.
Lỗi CORS “huyền thoại” khi mở file HTML trực tiếp và cách khắc phục trong 1 nốt nhạc
Một kịch bản rất quen thuộc: Bạn viết xong code, click đúp vào file index.html để mở trên Chrome. Bùm! Màn hình trắng tinh và Console báo lỗi đỏ chót liên quan đến CORS. Vậy lỗi CORS khi dùng module JavaScript và cách khắc phục? thực chất là gì? Trình duyệt có cơ chế bảo mật nghiêm ngặt, nó chặn việc load các module thông qua giao thức file:// cục bộ trên máy tính.
Cách khắc phục duy nhất và chuẩn xác nhất là bạn phải chạy code thông qua một môi trường server ảo (Local Server) với giao thức http://. Nếu dùng VSCode, bạn chỉ cần cài extension “Live Server” và nhấn nút “Go Live” là xong. Lỗi CORS này cũng là “khách quen” khi bạn giao tiếp với server bên ngoài, bạn có thể tham khảo thêm bài Fetch API gọi REST API bằng JavaScript để biết cách xử lý triệt để các vấn đề về giao thức mạng.
Kinh nghiệm “xương máu”: Tổ chức cấu trúc file và module sao cho khoa học?
Câu hỏi Tổ chức mã nguồn với JavaScript module? sao cho dễ bảo trì luôn là bài toán khó. Theo chuẩn phát triển web hiện đại, bạn nên phân chia rõ ràng thành các thư mục chức năng như /components (chứa giao diện), /utils (chứa hàm tiện ích), /api (chứa logic gọi data) và /assets (hình ảnh, css).
Ví dụ, các hàm chuyên xử lý lưu trữ dữ liệu trên trình duyệt nên được tách biệt hoàn toàn. Việc hiểu rõ Local Storage Session Storage JavaScript sẽ giúp bạn xây dựng một module storageUtil.js cực kỳ hữu dụng và gọi nó ở bất cứ đâu. Khi dự án lớn hơn, việc kết hợp module ES6 với các công cụ quản lý dependency và đóng gói như Webpack, Vite hay cấu hình "type": "module" trong môi trường Node.js sẽ giúp kiến trúc hệ thống của bạn vững như bàn thạch.
Tại sao phải dùng module? Đây là những lợi ích khiến bạn không thể chối từ

Ưu điểm của JavaScript module? Nó mang lại khả năng tái sử dụng code vượt trội, bảo vệ phạm vi biến an toàn tuyệt đối, và cho phép chia nhỏ một ứng dụng khổng lồ thành các phần nhỏ gọn, dễ dàng quản lý và nâng cấp.
Chia để trị: Giúp mã nguồn dễ thở và dễ bảo trì hơn
Tính module hóa sinh ra để giúp chúng ta phá vỡ một file code “quái vật” hàng ngàn dòng thành những mảnh ghép nhỏ gọn, logic gọn gàng. Mỗi file module chỉ nên đảm nhận một nhiệm vụ duy nhất (nguyên tắc Single Responsibility). Điều này giúp một team nhiều người có thể làm việc song song trên cùng một dự án mà không lo dẫm chân hay ghi đè code của nhau.
Khi có bug xảy ra ở phần giỏ hàng, bạn biết chính xác chỉ cần mở file cartModule.js để kiểm tra. Nó tiết kiệm hàng giờ đồng hồ mò mẫm trong một mớ code hỗn độn, giúp việc bảo trì và mở rộng tính năng trong tương lai trở nên nhẹ nhàng hơn bao giờ hết.
Tái sử dụng code mọi lúc mọi nơi mà không sợ “đụng hàng”
Khả năng tái sử dụng mã (Reusability) là một điểm cộng khổng lồ của ES6 Modules. Bạn chỉ cần tốn công viết một hàm tính toán thuế phức tạp một lần, lưu nó vào taxCalculator.js, và sau đó import nó ở bất kỳ màn hình nào, thậm chí copy sang hẳn một dự án khác để dùng lại.
Các module hoạt động hoàn toàn độc lập, giữ cho dữ liệu và logic bên trong nó được an toàn. Cơ chế đóng gói này có nét tương đồng rất lớn với cách hoạt động của closure trong việc bảo vệ tính riêng tư của biến. Nếu bạn muốn tìm hiểu sâu hơn về cơ chế bảo vệ dữ liệu nội bộ này, bài viết Closure JavaScript giải thích dễ hiểu sẽ cung cấp cho bạn một góc nhìn kỹ thuật cực kỳ thú vị và nền tảng.
Dọn dẹp Global Scope: Chấm dứt kỷ nguyên “ô nhiễm” biến toàn cục
Nhớ lại thời kỳ trước khi có ES6 Modules hay các giải pháp server-side như CommonJS, mọi biến bạn khai báo bằng var ở các file script khác nhau đều nhảy thẳng vào phạm vi toàn cục (window object trên trình duyệt). Điều này dẫn đến thảm họa “ô nhiễm” biến: file A vô tình ghi đè giá trị biến của file B chỉ vì trùng tên, gây ra những lỗi ngầm cực kỳ khó debug.
Với kiến trúc module, mọi thứ bạn khai báo (biến, hàm, class) đều bị nhốt chặt trong phạm vi cục bộ của file đó. Trừ khi bạn chủ động dùng từ khóa export để phơi bày nó ra ngoài, không một file nào khác có thể can thiệp hay thay đổi được. Code của bạn nay đã sạch sẽ, minh bạch và an toàn tuyệt đối.
Đấy, chỉ cần nắm vững vài chiêu import/export là bạn đã có thể sắp xếp lại toàn bộ cấu trúc code của mình gọn gàng và chuyên nghiệp hơn hẳn. Đừng coi đây chỉ là mớ lý thuyết khô khan, hãy thử tách ngay một file utils trong project hiện tại của bạn thành module xem sao. Tổ chức code tốt ngay từ đầu sẽ cứu bạn khỏi những đêm OT mệt mỏi dò bug sau này, tin mình đi.
Bạn có “tuyệt chiêu” hay quy tắc ngầm nào khi đặt tên và cấu trúc các file module trong dự án không? Hay bạn đang gặp rắc rối với việc cấu hình Webpack cho ES6? Đừng ngần ngại chia sẻ ở phần bình luận bên dưới để anh em dev cùng thảo luận và học hỏi nhé!
Lưu ý: Các thông tin trong bài viết này chỉ mang tính chất tham khảo. Để có được lời khuyên tốt nhất, vui lòng liên hệ trực tiếp với chúng tôi để được tư vấn cụ thể dựa trên nhu cầu thực tế của bạn.