Node thực chất là gì
Node.js là một chương trình bạn cài trên máy tính để chạy các tệp JavaScript. Đó là cách mô tả đơn giản nhất, và nó chính xác. Khi bạn gõ node script.js, Node đọc tệp, giao nó cho engine V8 của Google (engine JavaScript của Chrome) và thực thi, kèm theo một thư viện lớn các API bổ sung cho những việc V8 không tự làm được.
V8 biết cách chạy JavaScript. Nó không biết cách mở một tệp, lắng nghe trên một TCP socket, tạo một tiến trình hay đọc biến môi trường. Node cung cấp tất cả những thứ đó, được viết bằng C++, và đưa chúng đến code JavaScript của bạn dưới dạng các module có sẵn.
node --version
node script.js
Vậy Node không phải là một ngôn ngữ. Cũng không phải một framework. Nó là một runtime: V8 cộng với thư viện chuẩn, hệ thống module và một event loop. Chỉ có vậy.
Script đầu tiên
Mọi tệp .js đều là một chương trình Node hợp lệ. Không cần code khuôn mẫu, không cần hàm main:
console.log hoạt động giống như trong trình duyệt. Template literal, Date, mảng, promise: mọi tính năng ngôn ngữ bạn đã biết đều đến từ V8 và hoạt động y hệt. Điều thay đổi trong Node là những gì có sẵn xung quanh ngôn ngữ.
Biến toàn cục chỉ có trong Node
Trình duyệt có window, document, localStorage, fetch. Node có một bộ biến toàn cục khác, được thiết kế cho vai trò runtime phía máy chủ:
processlà tiến trình Node đang chạy. Nó chứa biến môi trường (process.env), đối số dòng lệnh (process.argv) và các phương thức để thoát (process.exit(1)).__filenamevà__dirnamecho bạn đường dẫn tuyệt đối của tệp hiện tại và thư mục chứa nó. (Trong ES module chúng không tồn tại, bạn dùngimport.meta.urlthay thế.)globallà object cấp cao nhất, tương đươngwindowtrong Node.
Không có document và không có window. Nếu bạn cố dùng chúng, bạn sẽ nhận ReferenceError. Đó thường là dấu hiệu đầu tiên cho thấy một thư viện được viết cho trình duyệt và sẽ không chạy được trong Node nếu giữ nguyên.
Đối số dòng lệnh và biến môi trường
Một phần lớn những gì Node được dùng để làm, như CLI, script build, máy chủ, cần đọc đối số và biến môi trường. Cả hai đều nằm trên process:
process.argv là một mảng: hai phần tử đầu là đường dẫn đến tệp thực thi của Node và đường dẫn script, nên đối số thật bắt đầu từ chỉ số 2. process.env là một object thường chứa các biến môi trường: đọc NODE_ENV, PORT hay API key từ đây là cách làm chuẩn.
Các module có sẵn
Node đi kèm một thư viện chuẩn, truy cập qua require (CommonJS) hoặc import (ESM). Tên module bắt đầu bằng node: để thể hiện rõ chúng là module có sẵn:
Những module bạn sẽ dùng nhiều nhất:
node:fs: đọc và ghi tệp. Dùngnode:fs/promisescho phiên bản async/await.node:path: nối, phân giải và phân tích đường dẫn tệp theo cách chạy được trên mọi hệ điều hành.node:http/node:https: xây dựng máy chủ HTTP và gửi request.node:url: phân tích và tạo URL.node:os: thông tin về máy đang chạy.node:crypto: băm, tạo byte ngẫu nhiên, mã hóa.
Bạn không cần cài những module này. Chúng đi kèm với Node. Mọi thứ khác đến từ npm.
Sơ lược về event loop
Node chạy JavaScript của bạn trên một luồng duy nhất, nhưng xử lý được nhiều việc cùng lúc. Bí quyết nằm ở event loop. Khi bạn gọi một thao tác bất đồng bộ như đọc tệp, gửi request HTTP, đặt bộ hẹn giờ, Node giao phần việc thực tế cho hệ điều hành (hoặc cho thread pool của nó) và tiếp tục thực thi. Khi việc hoàn tất, một callback được đưa vào hàng đợi, và vòng lặp sẽ lấy nó ra khi code hiện tại chạy xong.
Thứ tự được in ra là 1, 4, 2, 3. Code đồng bộ chạy trước. Sau đó đến microtask (các promise đã resolve). Rồi mới đến bộ hẹn giờ. Đó là lý do một vòng lặp nặng về CPU chạy chậm sẽ chặn cả máy chủ: chỉ có một luồng cho code của bạn. Khả năng xử lý đồng thời của Node dành cho I/O, không phải cho tính toán.
Một máy chủ HTTP nhỏ
Thành quả của tất cả những điều trên là một máy chủ web chạy được chỉ nằm gọn trong vài dòng:
Không framework, không thư viện phụ thuộc. createServer nhận một hàm chạy cho mỗi request; listen khởi động event loop để xử lý các kết nối đến. Ứng dụng thực tế dùng Express hoặc Fastify bên trên, nhưng bên dưới vẫn là module http có sẵn đó.
Node và trình duyệt
Nên nói rõ những gì dùng chung được và những gì không:
| Dùng được ở cả hai | Chỉ có trong Node | Chỉ có trong trình duyệt |
|---|---|---|
Tính năng ngôn ngữ (lớp, promise, async/await) | fs, http, process, __dirname | window, document, DOM |
console.log | CommonJS require / các điểm riêng của ESM trong Node | localStorage, sessionStorage |
fetch (Node 18+) | Truy cập hệ thống tệp và network socket | Sự kiện người dùng, hiển thị giao diện |
setTimeout, setInterval | Tiến trình con, stream | History API, navigator |
Node hiện đại đã có thêm một số API của trình duyệt như fetch, URL, AbortController, structuredClone, nên khoảng cách đã hẹp hơn trước. Nhưng DOM sẽ không có trong Node, và hệ thống tệp sẽ không có trong trình duyệt.
Node, Deno và Bun
Node là lựa chọn mặc định, nhưng không còn là JavaScript runtime duy nhất. Deno và Bun là các lựa chọn thay thế: Deno đến từ chính người tạo ra Node, Bun đến từ một nhóm mới hơn tập trung vào tốc độ. Chúng chạy JavaScript (và cả TypeScript một cách trực tiếp), có sẵn công cụ như trình chạy test và bundler, và khác Node ở cách xử lý module, quyền truy cập và cài đặt package.
Để học JavaScript, Node vẫn là nơi tập trung tài liệu, hướng dẫn và tin tuyển dụng. Các khái niệm như event loop, module, API có sẵn gần như chuyển được hoàn toàn sang các runtime khác. Hãy học Node trước; tìm hiểu các runtime khác khi dự án cần đến.
Chạy script nhanh
Vài cách để thực sự chạy code trong lúc làm việc:
# Run a file
node script.js
# Run a one-liner
node -e "console.log(2 ** 10)"
# Open the REPL (interactive prompt)
node
# Watch a file and re-run on save (Node 18.11+)
node --watch script.js
REPL là nơi nháp tiện lợi khi bạn muốn xem một phương thức trả về gì mà không cần tạo tệp. --watch rất hữu ích khi phát triển: lưu tệp, và Node tự động chạy lại script.
Tiếp theo: bắt lỗi
Chạy code là một chuyện; xử lý khi có sự cố là chuyện khác. Đọc tệp thất bại, request HTTP hết thời gian chờ, parse JSON ném lỗi. Chương tiếp theo nói về try/catch, các kiểu lỗi và những mẫu xử lý khi mọi thứ hỏng, mà trong một chương trình Node thì sớm muộn gì mọi thứ cũng sẽ hỏng.
Câu hỏi thường gặp
Node.js runtime là gì?
Node.js là một chương trình chạy JavaScript bên ngoài trình duyệt. Nó gói engine V8 của Google (chính engine chạy JS trong Chrome) cùng một lớp C++ cung cấp những API mà V8 không tự có: truy cập hệ thống tệp, mạng, tiến trình, bộ hẹn giờ. Sự kết hợp này cho phép bạn xây dựng máy chủ, công cụ dòng lệnh (CLI) và công cụ build bằng JavaScript.
Node khác trình duyệt thế nào?
Cả hai đều chạy JavaScript, nhưng các API xung quanh ngôn ngữ thì khác nhau. Trình duyệt cho bạn window, document và DOM. Node cho bạn process, fs, http, __dirname và cơ chế nạp module CommonJS/ESM. Node không có DOM và trình duyệt không có hệ thống tệp: ngôn ngữ dùng chung, nền tảng thì không.
Node.js là framework hay runtime?
Là runtime. Bản thân Node không áp đặt cấu trúc ứng dụng nào: nó chỉ thực thi JavaScript và cung cấp các API. Các framework như Express, Next.js hay NestJS được xây dựng trên Node. Deno và Bun là các JavaScript runtime khác cạnh tranh với Node nhưng đóng cùng vai trò.
Event loop trong Node là gì?
Event loop là thứ cho phép Node xử lý nhiều việc cùng lúc trên một luồng duy nhất. Khi bạn gọi một thao tác bất đồng bộ như đọc tệp, gửi request HTTP hay setTimeout, Node giao việc đó cho hệ thống và tiếp tục chạy. Khi việc hoàn tất, callback của nó được đưa vào hàng đợi và event loop sẽ lấy ra xử lý. Đó là lý do fs.readFile không chặn phần còn lại của chương trình.