Hướng Dẫn Chi Tiết Về Hệ Thống Module Trong ES6

Mỗi tệp tin JavaScript (JS) trong hệ thống hiện đại đều hoạt động như một đơn vị đóng gói riêng biệt, sở hữu không gian tên (scope) độc lập. Để các thành phần này có thể giao tiếp và chia sẻ dữ liệu mà không gây xung đột, chuẩn ES6 đã giới thiệu cơ chế module mạnh mẽ với cú pháp importexport.

1. Xuất và Nhập theo Dạng Có Tên (Named Exports)

Khi cần chia sẻ nhiều biến, hàm hoặc đối tượng từ một tệp tin, cách phổ biến nhất là sử dụng danh sách các tên cụ thể.

Tại file nguồn (ví dụ: moduleUtils.js):

// Khai báo dữ liệu
const userName = 'Tran Van B';
const userEmail = 'b@gmail.com';

// Hàm tính toán
function tinhGiaTri(tien, khuyenMai){
    return (tien - khuyenMai) * 1.1;
}

// Danh sách các mục cần expose ra ngoài
export { userName, userEmail, tinhGiaTri };

Tại file sử dụng:

// Bắt buộc tên phải khớp chính xác với export
import { userName, userEmail, tinhGiaTri } from './moduleUtils.js';

console.log(userName); // Tran Van B
console.log(tinhGiaTri(100, 10)); 

// Hỗ trợ đặt tên lại khi import để tránh trùng lặp
import { userName as myUser, tinhGiaTri as costCalc } from './moduleUtils.js';
console.log(myUser);
Lưu ý quan trọng: Nếu bạn sử dụng export { ... } để liệt kê các mục cụ thể, thì tại nơi nhập liệu tuyệt đối không được dùng dạng import X from ... (dạng mặc định) nếu không kết hợp thêm export default. Điều này sẽ dẫn đến lỗi undefined hoặc thiếu sót dữ liệu. Hãy đảm bảo cú pháp import tương thích với loại export được định nghĩa.

2. Xuất Trực Tiếp Khi Khai Báo (Export Declaration)

Bạn cũng có thể gắn trực tiếp từ khóa export vào đầu dòng khai báo. Cách này thường thấy khi muốn tạo giao diện API công khai cho một hằng số hoặc hàm ngay lập tức.

File nguồn:

export let globalConfig = "default_theme";

File nhận dữ liệu:

import { globalConfig } from './settings.js';

3. Quản Trị Hàm và Class

Cơ chế module hỗ trợ đầy đủ việc xuất các cấu trúc phức tạp như hàm hoặc lớp (class).

Xuất hàm:

export function xuLyDuLieu(data) {
    return data.toUpperCase();
}

import { xuLyDuLieu } from './core.js';
console.log(xuLyDuLieu('test'));

Xuất Class:

export class NhanVien {
    constructor(name) {
        this.name = name;
    }
    lamViec() {
        console.log(this.name + ' đang làm việc');
    }
}

// Khởi tạo từ class đã xuất
import { NhanVien } from './company.js';
const nhanVien1 = new NhanVien('Lan');
nhanVien1.lamViec();

4. Cơ Chế Mặc Định (Default Export)

Sử dụng export default khi bạn chỉ muốn lộ ra một giá trị chính của module và mong muốn người dùng linh hoạt đặt tên cho nó.

Đặc điểm kỹ thuật:

  • Một tệp tin chỉ được phép chứa duy nhất một lệnh export default.
  • Lúc import không cần dấu ngoặc nhọn {}.
  • Tên biến nhận vào có thể tùy ý chọn, không bắt buộc giống tên gốc.

File nguồn:

const diaChiHanoi = 'Hà Nội';
export default diaChiHanoi;

// Hoặc xuất hàm luôn
export default function getGreeting() {
    return 'Xin chào thế giới';
}

File nhận dữ liệu:

// Tên 'address' hoàn toàn do developer quyết định
import address from './diaChi.js';
console.log(address); // Hà Nội

// Hoặc gọi hàm mặc định ngay lập tức
import handler from './functonLib.js';
handler();

Với trường hợp cần xuất nhiều thứ nhưng vẫn muốn dùng mặc định, ta có thể gom chúng vào một đối tượng:

const obj = {
    hamMot: () => {},
    hamHai: () => {}
};
export default obj;

5. Nhập Toàn Bộ Thành Phần (Namespace Import)

Thay vì lấy lẻ từng thuộc tính, ta có thể import toàn bộ nội dung của module dưới dạng một đối tượng chứa tất cả các thành phần đã export (kể cả named và default).

import * as DataModule from './dataSource.js';

// Truy cập thông qua prefix
console.log(DataModule.userName); 
console.log(DataModule.globalConfig);

6. Chạy Mã Nguồn (Side Effects Import)

Trong một số kịch bản như khởi tạo thư viện hoặc chạy các script hiệu chỉnh môi trường, bạn chỉ muốn thực thi mã bên trong tệp tin đó mà không cần lấy giá trị trả về.

File m2.js (chứa vòng lặp, không export gì cả):

for(let i = 0; i < 3; i++){
    console.log('Vòng lặp:', i);
}

File chính:

import './m2.js'; // Mã trên sẽ tự động chạy khi import dòng này

7. Tổng Kết & Khuyến Nghị

Cả export thường quy và export default đều có chức năng phơi bày dữ liệu. Sự khác biệt cốt lõi nằm ở chỗ export yêu cầu sự đồng bộ chặt chẽ giữa tên xuất và tên nhập, trong khi export default tập trung vào một đơn vị sản phẩm chính.

  • Quy tắc đặt tên: Named exports ({name}) giúp rõ ràng, tối ưu hóa việc trích xuất (tree-shaking) trong build tool. Đây là lựa chọn tốt nhất cho các thư viện utility.
  • Lý tưởng cho App: Default export thường thuận tiện cho các component UI lớn hoặc khi bạn muốn giấu đi cấu trúc bên trong.
  • Hạn chế: Không nên pha trộn quá nhiều style export trong cùng một project vì khó bảo trì. Nên thống nhất một tiêu chuẩn trong toàn team.

Thẻ: JavaScript es6 modules web-development

Đăng vào ngày 28 tháng 6 lúc 08:49