Published on

CommonJS, ES Modules, UMD, AMD, Universal... sự hỗn loạn của JavaScript

Có bao giờ quý vị cảm thấy bối rối với CommonJS, ES Modules, Import, Require trong JavaScript chưa?, đôi khi rối ren bởi .js, .mjs, .cjs... nữa. Trong bài viết này, tại hạ sẽ giải thích rõ ràng nhất về chúng.
CommonJS, ES Modules, UMD, AMD, Universal... sự hỗn loạn của JavaScript
Authors
  • Name
    Nguyen Pham

Tổng quan

Nếu là một lập trình viên JavaScript, chắc hẳn quý vị đã từng gặp phải những vấn đề với CommonJS, ES Modules, Import, Require trong JavaScript. Không ít người trong chúng ta đã từng rối bời với .js, .mjs, .cjs... chả hiểu nó là cái gì, sử dụng ra làm sao, viết mã thế nào... Để hiểu được nó thì xin phép được quay về nguồn gốc cái đã.

Nguyên nhân

Xưa thật là xưa,
nhớ mấy cho vừa,
nhớ chuyện thuở sơ khai...

Khi JavaScript ra đời, nó chỉ được sử dụng để thêm hiệu ứng cho trang web, xử lý form, kiểm tra dữ liệu... thậm chí nó chưa bao giờ được coi là một ngôn ngữ lập trình đích thực. Nhiều người còn gọi nó là một ngôn ngữ kịch bản (scripting language).

Ấy vậy mà, JavaScript đã trở thành một trong những ngôn ngữ lập trình phổ biến nhất hiện nay. Do sự phát triển của web, JavaScript đã trở nên mạnh mẽ hơn, nó được sử dụng để xây dựng ứng dụng web, ứng dụng di động, ứng dụng máy tính... thậm chí là ứng dụng máy chủ. Những nhu cầu đó đã khiến cho JavaScript phải phát triển mạnh mẽ đến độ vô song, và từ đó mà những vấn đề về modules, import, export... đã xuất hiện.

Do ngay từ đầu JavaScript không có một hệ thống module chính thức nào, nên chưa tạo ra sự phức tạp như hiện nay. Suốt nhiều năm cộng đồng JavaScript đã phải vật lộn với những hạn chế của nó và tự tay tạo ra các thể loại modules cho nó, trong đó có thể kể đến như là AMD, UMD…

Và rồi, Node.js ra đời như một cứu cánh cho JavaScript. Node.JS được thiết kế chủ yếu chạy ở môi trường máy chủ, nó lựa chọn CommonJS làm trình xử lý modules mặc định. Sự bùng nổ của Node.JS đã thay đổi toàn bộ cái nhìn và cách tiếp cận JavaScript, đồng thời kéo theo một loại thay đổi mang tính cách mạng cho ngành công nghệ.

Trong khi đó Ecma International có thể xem như là nơi chủ quản ngôn ngữ JavaScript hay còn gọi là ECMAScript đã không theo kịp nhịp điệu này. ECMAScript lại rất quan trọng cho trình duyệt vì nó được thiết kế để chạy hầu hết trên trình duyệt.

Nhận ra sự hớ hênh, trễ nải của mình ECMAScript cuối cùng đã chỉnh sửa sai lầm của mình bằng cách giới thiệu ES Modules. Tuy nhiên, mọi thứ đã quá muộn, CommonJS đã trở nên quá phổ biến và không thể loại bỏ được.

Đó chính là nguồn cơn cho những rắc rối về sau...

Định nghĩa

Trước tiên chúng ta cần hiểu một số định nghĩa cơ bản như sau:

Các loại module

TênChú thích
CommonJS (CJS):được Node.js sử dụng làm module system mặc định. Cú pháp requiremodule.exports được sử dụng để import và export module.
ES Modules (ESM):là một chuẩn module system của JavaScript, được giới thiệu từ ES6 (ES2015), hay còn gọi là ECMAScript. Cú pháp importexport được sử dụng để import và export module.

Cú pháp import và export

  • Import:
js
// CommonJS
const module = require('module');
js
// ES Modules
import module from 'module';
  • Export:
js
// CommonJS
module.exports = module;
js
// ES Modules
export default module;

Các loại file extension

  • .js: file JavaScript thông thường, có thể sử dụng cả CommonJS và ES Modules.
  • .jsx: file JavaScript thông thường, có thể sử dụng cả CommonJS và ES Modules. Nhiều người còn tưởn lầm nó chỉ dành cho React. Không phải, JSX là một phần mở rộng của JavaScript.
  • .ts: file TypeScript thông thường, có thể sử dụng cả CommonJS và ES Modules.
  • .tsx: file TypeScript sử dụng ES Modules.
  • .mjs: file JavaScript sử dụng ES Modules. Nhiều người còn gọi đùa nó là Micheal Jackson Script.
  • .cjs: file JavaScript sử dụng CommonJS.

Giải thích

Từ những ví dụ trên, đã bao giờ quý vị thắc mắc tại sao có lúc lại dùng được import, có lúc lại dùng require hay thậm chí tại sao khi bắt đầu viết một ứng dụng Node.js mới, quý vị lại thấy mình phải chọn giữa .js, .mjs, .cjs chưa?

require là cú pháp của CommonJS, chúng được sử dụng để làm hệ thống modules cho Node.js từ những ngày đầu. Bởi ngay từ đầu Node.js ra đời thì Javascript vẫn chưa có một hệ thống module chính thức nào cho ngôn ngữ này. Một đặc tính của CommonJS là chúng được thiết kế để "tải" các module một cách đồng bộ và điều này thực sự không tốt cho mô hình trình duyệt (browser), khi mà các module được tải đồng bộ sẽ khiến hiệu năng tải trang bị giảm đi đáng kể.

Khi ES6 ra đời. Nó đã giới thiệu một hệ thống module mới, đó chính là ES Modules lúc này nó đã mang đến hệ thống module chính thức cho Javascript. ES Modules được thiết kế để "tải" các module một cách bất đồng bộ, điều này giúp tăng hiệu năng tải trang.

Nói cách khác, CommonJS được sử dụng nhiều hơn ở ứng dụng phía máy chủ (Node.js) và ES Modules được sử dụng nhiều hơn ở ứng dụng phía client (browser).

Hỗ trợ trình duyệt

Hiện tại, ES Modules đã được hỗ trợ trên hầu hết các trình duyệt hiện đại như Chrome, Firefox, Safari, Edge, Opera. Tuy nhiên, để sử dụng ES Modules trên trình duyệt, chúng ta cần thêm thuộc tính type="module" vào thẻ <script>.

html
<script type="module" src="main.js"></script>

ECMASCript không hỗ trợ ES Modules trên Internet Explorer, nếu quý vị muốn sử dụng ES Modules trên IE hoặc các trình duyệt cũ hơn, quý vị cần sử dụng một công cụ chuyển đổi như Babel, Webpack, Rollup...

Sự ra đời của các phần mở rộng file .mjs.cjs

Do có nhiều hệ thống module khác nhau, nên việc sử dụng .js cho cả hai loại module làm cho việc phân biệt giữa CommonJS và ES Modules trở nên khó khăn. Vì vậy, ECMAScript đã quyết định thêm hai phần mở rộng file mới là .mjs.cjs để phân biệt giữa CommonJS và ES Modules.

Để bắt kịp xu thế, Node.js đã cập nhật việc hỗ trợ ESM từ phiên bản v12 LTS trở đi. Muốn vậy chúng ta cần thoả các điều kiện sau:

  • Sử dụng file .mjs hoặc .cjs để sử dụng ES Modules hoặc CommonJS.
  • Thêm "type": "module" vào package.json để sử dụng ES Modules trong file .js.
  • Để sử dụng ES Modules trong Node.js, chúng ta cần thêm cờ --experimental-modules khi chạy file .mjs hoặc .cjs. Ví dụ:
bash
node --experimental-modules index.mjs

Hoặc thêm "type": "module" vào package.json:

json
{
  "type": "module"
}

Universal modules

Universal modules là các module có thể hoạt động cả trên Node.js và trình duyệt. Nghĩa là tuỳ thuộc vào môi trường chúng ta sử dụng, thì nó sẽ tự động chuyển đổi giữa CommonJS và ES Modules. Để làm được điều này, chúng ta cần thêm một số cấu hình vào package.json:

json
{
  "main": "cjs/index.js",
  "module": "es6/index.js",
}

Vấn đề ở đây là chúng ta phải viết mã cho cả hai hệ thống module, điều này có thể làm tăng thời gian phát triển và bảo trì mã nguồn. Tuy nhiên với những công cụ build hiện tại đã có thể sinh ra mã cho cả hai môi trường. Nhưng với riêng cảm nhận của mình điều này vẫn chưa phải là một giải pháp tốt.

Kết luận

Chính việc có nhiều hệ thống module khác nhau đã tạo ra sự phức tạp cho ngôn ngữ JavaScript. Nhưng cũng chính vì thế mà JavaScript đã trở nên phong phú và trở thành vô song như hiện nay. Có lẽ hiện tại nó là ngôn ngữ duy nhất chạy được trên cả máy chủ và trình duyệt, một điều mà không phải ngôn ngữ nào cũng làm được.

CommonJS và ES Modules đều có những ưu điểm và nhược điểm riêng. CommonJS phù hợp với ứng dụng máy chủ, trong khi ES Modules phù hợp với ứng dụng trình duyệt. Vì vậy, chúng ta vẫn cần phải sử dụng cả hai trong một thời gian dài nữa.

Chính do sự phức tạp và không muốn phụ thuộc vào công nghệ của nhau mà thế giới JavaScript đã trở nên hỗn loạn như hiện nay. Nhưng có lẽ đây sẽ là giai đoạn quan trọng của ngôn ngữ này, khi mà nó đang dần trở nên mạnh mẽ và phổ biến hơn bao giờ hết. Biết đâu đấy, có lẽ trong tương lai, sẽ có một gương mặt mới xuất hiện giúp cho JavaScript trở nên hoà hợp, đơn giản và mạnh mẽ hơn.

Trời Thu Texas 2024, tại hạ kính bút.

Nguyen Pham

Nguyen Pham

Làm việc tại phòng thí nghiệm MADE, Texas, USA. Là một người đam mê với công nghệ và thích chia sẻ kiến thức với mọi người.

Nguyen Pham — là nhà phát triển và thiết kế giàu kinh nghiệm tập trung vào WordPress, NextJS, Angular. Hãy xem một số dự án chúng tôi đã thực hiện và các sản phẩm nội bộ của chúng tôi.
Liên kết
Made by VueJS and Vercel Cloud· All rights reserved.