Tìm hiểu về modules trong Node.js. Tại sao lại có nhiều loại modules như vậy?

Tìm hiểu về modules trong Node.js. Tại sao lại có nhiều loại modules như vậy?

Tin ngắn hàng ngày dành cho bạn
  • Manus đã chính thức mở cửa cho tất cả người dùng rồi đấy mọi người. Cho những ai chưa biết thì đây là một công cụ viết báo cáo (làm mưa làm gió) giống như Deep Research của OpenAI á. Mỗi ngày được miễn phí 300 Credits để nghiên cứu. Mỗi lượt nghiên cứu tiêu tốn tuỳ thuộc vào độ phức tạp của yêu cầu. À với cả họ đang có chương trình tặng miễn phí Credits hay sao á. Như mình thì vào thấy được hẳn 2000.

    Mình dùng thử, so sánh với cùng một lệnh giống như đợt trước dùng bên Deep Research thì nội dung khác biệt nhau hoàn toàn. Manus báo cáo như kiểu viết văn hơn so với OpenAI là các gạch đầu dòng và bảng biểu.

    À lúc đăng ký xong có bắt nhập số điện thoại để xác minh, nếu lỗi thì các bạn đợi qua ngày thử lại xem có được không nhé.

    » Xem thêm
  • Mọi người chắc nghe nhiều về xu hướng tìm kiếm thông tin bằng AI chứ không cần công cụ tìm kiếm như Google nữa rồi đúng không? Không đâu xa ánh xạ vào bản thân thì thấy đúng thật, thi thoảng mới tìm kiếm thôi chứ còn đâu toàn hỏi tụi AI.

    Ngay từ đầu viết blog, thứ mà mình hướng đến là chia sẻ kinh nghiệm chứ không phải là những bài mang nặng tính kỹ thuật, máy móc, hướng dẫn từ đầu... Vì thời điểm đó đã có quá nhiều người làm nội dung này rồi và họ làm rất tốt, tại sao mình phải cố phát minh lại bánh xe? Một điều nữa là tin tưởng độc giả của mình có khả năng tìm hiểu vấn đề. Nếu bạn đọc đủ nhiều các bài viết trên blog thì thấy mình luôn cố gắng chèn thêm các liên kết tham khảo ngoài bài viết, nêu ra vấn đề mở và rất ít khi kết luận chắc chắn một điều gì đó.

    Mình đã cố gắng rèn luyện kỹ năng viết, kỹ năng trình bày và cả cách tương tác với độc giả để mang lại giá trị cho họ. Nhiều lúc ngồi lật lại các con số thống kê thấy lượng đọc bài viết tăng lên lại cảm thấy vui. Nhưng khi nguồn truy cập đến từ Google thì lại thấy buồn, vì điều đó chứng tỏ họ biết đến mình chỉ khi đang cố đi tìm giải pháp, có thể họ chỉ đọc chớp nhoáng, may ra tìm được cách giải quyết và thế là đóng cửa sổ trình duyệt rồi đi như một cơn gió.

    Chừng vài tháng đổ lại đây, một điều khiến mình rất vui đó là lượng người truy cập thẳng vào trang chủ mà không thông qua công cụ tìm kiếm đang tăng dần lên, có nhiều hôm lượng truy cập tự nhiên còn cao hơn cả đến từ Google. Điều đó chứng tỏ độc giả đã có thói quen quay lại trang của mình nhiều hơn và họ tìm thấy được giá trị từ blog mang lại. Vui mừng khôn xiết 🤩

    Bên cạnh đó thì lượng truy cập vào chuyên mục Threads - tức là mục mình đang viết bài này đang cao hơn bao giờ hết. Điều đó chứng tỏ xu hướng đi theo tin nhanh là đúng đắn. Mình có thể ngồi cả ngày để viết tin ngắn cho bạn đọc vì nó rất nhanh mà tiện, không tốn công đi tìm tài liệu để viết, không tốn cả thời gian viết nữa, còn mình thì có rất nhiều thứ để chia sẻ 😅. Nhưng không vì thế mà bỏ bê các bài viết dài, vì dài thì có nhiều thông tin để chia sẻ hơn.

    Vài lời tâm sự thế thôi chứ hơn một tháng nay mình chưa viết bài viết mới nào vì công việc bận quá. Xong lâu dần cứ trì hoãn lại thành lười. À với cả tháng 5 này rất thích hợp để đọc các cuốn sách về cách mạng á. Có hôm đọc đến 2 giờ sáng mới đi ngủ 🥱

    » Xem thêm
  • Mình mới nhìn thấy một trang web khá thú vị nói về các cột mốc đáng nhớ trong lịch sử phát triển Internet toàn cầu: Internet Artifacts

    Chỉ từ 1977 - khi Internet còn nằm trong hộp thí nghiệm thì nhìn xem - giờ đây Internet đã khiến mọi thứ phát triển đến mức nào 🫣

    » Xem thêm

Vấn đề

Khi viết ứng dụng Vue.js hay React.js, bạn thường hay sử dụng cú pháp import để sử dụng một component hay một module nào đó. Còn với các ứng dụng Node.js thuần JS sẽ thường dùng require:

const fs = require('fs'); // CommonJS
// hay
import { Component } from 'React'; // ES6 module

Hoặc đôi khi bạn cũng có thể bắt gặp trường hợp khai báo module bằng cách sử dụng cú pháp của AMD (Asynchronous Module Definition) như ở dưới đây:

define(['jquery'] , function ($) {
    return function () {};
});

Từ những ví dụ trên, đã bao giờ các bạn 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 lại không thể sử dụng cú pháp import một cách bình thường?

// file index.js
import * as fs from "fs";
fs.mkdir('/temp/temp', () => console.log("success"));

Khởi chạy:

$ node index.js
(node:2912) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.  
/Users/hoaitx/index.js:1
import * as fs from "fs";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:931:16)
    at Module._compile (internal/modules/cjs/loader.js:979:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1035:10)
    at Module.load (internal/modules/cjs/loader.js:879:32)
    at Function.Module._load (internal/modules/cjs/loader.js:724:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

Thì một lỗi sẽ được trả ra như ở trên! Vậy sự khác biệt giữa chúng là gì và tại sao lại có những sự khác biệt đó? Bài viết dưới đây hy vọng sẽ giúp các bạn giải đáp được những thắc mắc ở trên.

1. CommonJS và AMD

Cho những ai chưa biết thì 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. Mục tiêu của CommonJS là đưa ra một hệ thống module cho các ứng dụng Node.js. 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ể.

Trái lại với CommonJS thì AMD được thiết kế để phù hợp hơn với môi trường trình duyệt. RequireJS là một triển khai tiêu biểu của AMD. Tính năng mới trong AMD là define() cho phép khai báo các phụ thuộc của một module nó trước khi được tải.

define('module/id/string', ['module', 'dependency', 'array'], 
function(module, factory function) {
    return ModuleContents;  
});

Việc tải module trong AMD có thể thực hiện một cách bất đồng bộ sẽ khiến cho hiệu năng tải trang được cải thiện hơn so với việc tải đồng bộ.

AMD modules

Tóm lại, CommonJS và AMD là các đặc tả module Javascript có các cách triển khai khác nhau. CommonJS được sử dụng nhiều hơn ở ứng dụng phía máy chủ (Node.js) còn AMD được sử dụng nhiều hơn ở ứng dụng phía máy khách (trình duyệt).

AMD thuở ban đầu là một đặc tả dự thảo cho định dạng module trong của CommonJS, nhưng không đạt được sự đồng thuận của cộng đồng phát triển thế nên nó được chuyển sang nhóm amdjs. Các tranh luận chủ yếu xoay quanh việc tìm ra định dạng nào là tốt hơn, và cuối cùng CommonJS được tạo ra cho sự phát triển ứng dụng phía máy chủ vì tính chất đồng bộ của nó và AMD phù hợp hơn cho phát triển phía máy khách (trình duyệt) với tính chất không đồng bộ. *Tham khảo*.

2. ES6 Module (ESM)

Và từ khi ES6 được giới thiệu, lúc này nó đã mang đến hệ thống module chính thức cho Javascript. Vậy thì mọi thứ về module đã được giải quyết? Cú pháp của ESM chủ yếu xoay quanh importexport các từ khoá:

import * as fs from "fs"; // import
export * from "fs"; // export

ECMAScript (ES) cũng chỉ là một đặc tả và việc áp dụng những đặc tả đó là do các nhà phát triển phần mềm (trình duyệt web, Node.js...), hay thậm chí có những ứng dụng cũ hơn đã bị ngưng cập nhật (trình duyệt IE) và chúng hoàn toàn không có khả năng để xử lý những tính năng mới nhất của ES. Chính vì thế, hiện nay chúng ta đã có khá nhiều công cụ hỗ trợ cho việc chạy mã JS ở các phiên bản Node.js, trình duyệt cũ hơn. Điều đó giúp cho chúng ta không cần quá quan tâm về việc sử dụng những cú pháp mới hơn của ES.

Một số công cụ như Babel, Webpack có tích hợp công cụ hỗ trợ chúng ta chuyển đổi mã từ ES6 trở đi thành mã ES5 mà đa số trình duyệt cũ có thể hiểu được.

Để bắt kịp xu thế, Node.js đã cập nhật việc hỗ trợ ESM từ phiên bản v12 LTS tuy nhiên cho đến hiện tại (v14 LTS) ESM vẫn đang là tính năng được thực nghiệm.
Có một số cách để "hợp thức hoá" việc sử dụng ESM trong các phiên bản Node.js từ 12 trở lên, bao gồm:

  • Đặt tên file có phần mở rộng .mjs
  • Thêm thuộc tính "type" có giá trị là "module" trong file package.json
  • Hoặc sử dụng flag --input-type trong command.
// file index.mjs
import * as fs from "fs";
fs.mkdir('/temp/temp', () => console.log("success"));
$ node index.mjs
(node:3933) ExperimentalWarning: The ESM module loader is experimental.  
success

3. Tổng kết

CommonJS và AMD đã những đặc tả về module đã ra đời từ trước khi ESM được giới thiệu kể từ ES6. ESM mới thực sự là hàng "chính chủ", nhưng sự phổ biến của CommonJS đã khiến cho nó trở thành module mặc định của Node.js.

Ngày nay, với sự phát triển của các công cụ hỗ trợ dịch mã từ ES cao hơn xuống ES thấp hơn khiến cho việc viết mã Javascript như đang dùng cú pháp mới nhưng đằng sau nó là mã đã được dịch về phiên bản ES cũ hơn để tương thích với hầu hết trình duyệt, Node.js phiên bản cũ hơn.

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (0)

Nội dung bình luận...