0
0
Lập trình
Harry Tran
Harry Tran106580903228332612117

Tại Sao Một Số Gói Cần `import * as …` Thay Vì `import …`?

Đăng vào 7 tháng trước

• 5 phút đọc

Tại Sao Một Số Gói Cần import * as … Thay Vì import …?

Khi bạn bắt đầu làm việc với JavaScript hoặc TypeScript, bạn sẽ thấy hai phong cách khác nhau của việc nhập thư viện:

javascript Copy
// Phong cách 1
import * as moment from "moment";

// Phong cách 2
import moment from "moment";

Nhìn sơ qua, hai phong cách này có vẻ giống nhau. Nhưng tùy thuộc vào dự án, một phong cách có thể hoạt động trong khi phong cách còn lại gây ra lỗi.

Vậy tại sao một số gói “yêu cầu” import * as? Và khi nào bạn có thể sử dụng phong cách import … sạch hơn? Câu trả lời nằm ở hệ thống modulecài đặt trình biên dịch TypeScript.


Hệ Thống Module: CommonJS vs ES Modules

Có hai cách chính để mã JavaScript được đóng gói:

1. CommonJS (CJS)

Hệ thống Node.js ban đầu. Sử dụng requiremodule.exports.

javascript Copy
// moment/index.js
module.exports = moment;
javascript Copy
// Sử dụng require
const moment = require("moment");

2. ES Modules (ESM)

Tiêu chuẩn JavaScript hiện đại. Sử dụng importexport.

javascript Copy
// mô-đun kiểu hiện đại
export default PDFDocument;

// Sử dụng import
import PDFDocument from "pdfkit";

👉 Hầu hết các thư viện cũ như Moment.js, PDFKit, và Lodash được viết bằng CommonJS, không phải ESM.


import * as … Làm Gì

Khi bạn viết:

javascript Copy
import * as moment from "moment";

Điều này có nghĩa là:

“Nhập toàn bộ đối tượng module và gán nó cho biến moment.”

Nếu module chỉ xuất một thứ (như Moment.js), bạn vẫn sẽ nhận được toàn bộ đối tượng dưới cái tên đó.

✅ Điều này luôn hoạt động với các thư viện CommonJS.
❌ Nó có vẻ hơi dài dòng.


import … Làm Gì

Khi bạn viết:

javascript Copy
import moment from "moment";

Điều này có nghĩa là:

“Nhập xuất mặc định từ module.”

Nhưng đây là vấn đề: Các thư viện CommonJS không thực sự có xuất mặc định.

Đó là lý do tại sao trong TypeScript bạn cần bật một số cờ trong file tsconfig.json:

json Copy
{
  "compilerOptions": {
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}

Các tùy chọn này cho TypeScript biết:
👉 “Giả sử rằng các module CommonJS xuất một đối tượng mặc định để chúng tôi có thể sử dụng import x from "y";.”


Ví Dụ Thực Tế

Hãy xem cách điều này diễn ra với một vài thư viện phổ biến.

Ví Dụ 1: Moment.js

javascript Copy
// Hoạt động mọi nơi
import * as moment from "moment";

console.log(moment().format("YYYY-MM-DD"));
javascript Copy
// Sạch hơn, nhưng yêu cầu esModuleInterop
import moment from "moment";

console.log(moment().format("YYYY-MM-DD"));

Ví Dụ 2: PDFKit

javascript Copy
// Nhập không gian tên
import * as PDFDocument from "pdfkit";

const doc = new PDFDocument();
doc.text("Hello, PDFKit!");
doc.end();
javascript Copy
// Nhập xuất mặc định (cần esModuleInterop)
import PDFDocument from "pdfkit";

const doc = new PDFDocument();
doc.text("Hello, PDFKit với nhập xuất mặc định!");
doc.end();

Ví Dụ 3: Lodash

javascript Copy
// Nhập không gian tên
import * as _ from "lodash";

console.log(_.capitalize("hello world"));
javascript Copy
// Nhập xuất mặc định (cần esModuleInterop)
import _ from "lodash";

console.log(_.capitalize("hello world"));

Tại Sao Một Số Dự Án Sử Dụng * as …

  • Họ có cài đặt TypeScript nghiêm ngặt (esModuleInterop: false).
  • Họ muốn giữ tương thích với các module CommonJS nguyên bản.
  • Các cơ sở mã cũ thường giữ phong cách này.

Tại Sao Một Số Dự Án Sử Dụng Nhập Xuất Mặc Định

  • Họ đã bật esModuleInterop: true.
  • Họ thích cú pháp sạch hơn:
javascript Copy
import moment from "moment";
import PDFDocument from "pdfkit";
import _ from "lodash";
  • Nó phù hợp với các quy tắc ESM hiện đại.

Bạn Nên Sử Dụng Cái Nào?

  • Đối với các dự án mới: bật esModuleInterop trong tsconfig.json và sử dụng import x from "package".
  • Đối với các dự án cũ hoặc nghiêm ngặt: sử dụng import * as x from "package".
  • Đối với các thư viện hiện đại (như Luxon, Axios, v.v.): luôn sử dụng import x from "package" — chúng được viết dưới dạng ES Modules.

Sự khác biệt không phải về Moment.js, PDFKit hay Lodash. Nó liên quan đến cách TypeScript cầu nối khoảng cách giữa CommonJS và ES Modules.

  • import * as X → Nhập toàn bộ đối tượng module (an toàn cho tất cả các gói CommonJS).
  • import X → Nhập xuất mặc định (chỉ hoạt động nếu bạn bật esModuleInterop).

👉 Vì vậy, nếu một gói có vẻ “yêu cầu” * as, đó không phải là lỗi của gói — đó chỉ là cấu hình module của dự án của bạn.


Thực Hành Tốt Nhất

  • Nên sử dụng import ... from ... cho các dự án mới để giữ cho mã nguồn sạch sẽ và dễ đọc.
  • Kiểm tra các gói thư viện của bạn để đảm bảo tương thích với cài đặt TypeScript hiện tại.

Các Cạm Bẫy Thường Gặp

  • Không cấu hình đúng tsconfig.json có thể dẫn đến lỗi không mong muốn khi nhập thư viện.
  • Sử dụng kiểu nhập không chính xác có thể gây khó khăn trong việc duy trì mã.

Mẹo Hiệu Năng

  • Nên sử dụng các thư viện hiện đại được viết bằng ES Modules để tận dụng tối đa hiệu suất và tính năng mới nhất.
  • Tránh nhập toàn bộ không gian tên nếu bạn chỉ cần một phần nhỏ của thư viện.

Giải Quyết Vấn Đề

  • Nếu bạn gặp lỗi khi sử dụng import ..., kiểm tra cài đặt tsconfig.json và đảm bảo rằng esModuleInterop được bật nếu bạn muốn sử dụng nhập xuất mặc định.

Câu Hỏi Thường Gặp

1. Tại sao tôi không thể sử dụng import ... với một thư viện CommonJS?

Bạn cần đảm bảo rằng esModuleInterop được bật trong tsconfig.json để sử dụng cú pháp này.

2. Tôi có thể chuyển đổi dự án cũ sang ES Module không?

Có, nhưng bạn cần phải kiểm tra các thư viện và điều chỉnh mã của mình cho phù hợp với cú pháp mới.

3. Có cách nào để kiểm tra xem một gói có hỗ trợ nhập xuất mặc định không?

Bạn có thể xem tài liệu của gói hoặc kiểm tra mã nguồn để xác định cách nó xuất các module.


Hy vọng rằng bài viết này đã giúp bạn hiểu rõ hơn về sự khác nhau giữa import * asimport ..., cũng như cách sử dụng chúng đúng cách trong dự án của bạn. Hãy thực hành và áp dụng những gì bạn đã học để cải thiện kỹ năng lập trình của mình!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào