Giới thiệu
Bạn có bao giờ tự hỏi điều gì xảy ra sau hậu trường của trình duyệt khi bạn nhập URL và nhấn Enter? Từ bên ngoài, mọi thứ có vẻ đơn giản, nhưng ẩn sau đó là một quá trình kỹ thuật rất phức tạp với nhiều bước, lớp và giao thức. Trong bài viết này, chúng ta sẽ khám phá cách thức hoạt động của trình duyệt và cách mà kiến trúc bên trong của chúng được thiết kế.
Thành phần của một Trình duyệt
Một trình duyệt có thể được hiểu và chia thành nhiều phần quan trọng:
Giao diện Người Dùng (UI)
Là lớp tương tác với người dùng, bao gồm mọi thứ bạn nhìn thấy và tương tác trực tiếp, chẳng hạn như thanh địa chỉ, nút điều hướng và các tab.
Bộ máy Trình duyệt
Bộ máy trung tâm của trình duyệt. Nó kết nối giao diện người dùng với các thành phần khác của trình duyệt. Nó cũng quản lý việc lưu trữ dữ liệu (cookies, cache, localStorage) và giao tiếp với Bộ xử lý nội dung.
Bộ xử lý Nội dung
Chịu trách nhiệm hiển thị nội dung trên màn hình. Thành phần này chuyển đổi HTML, CSS và JavaScript thành các pixel có thể nhìn thấy, làm việc cùng với trình thông dịch JS và lớp mạng.
Mạng
Quản lý các yêu cầu HTTP/HTTPS, phân giải DNS, cookies, cache, nén dữ liệu và tất cả các biện pháp bảo mật của kết nối.
Trình thông dịch JavaScript
Phân tích, biên dịch và thực thi mã JS: phân tích, tạo Cây cú pháp trừu tượng (AST), chuyển đổi thành bytecode, áp dụng tối ưu hóa JIT và chạy nó trên VM JS. Điều này cho phép thao tác DOM, xử lý sự kiện và tương tác trang.
Lưu trữ Dữ liệu
Cho phép trình duyệt lưu trữ và truy xuất dữ liệu cục bộ, đảm bảo dữ liệu người dùng, cài đặt và sở thích được duy trì giữa các phiên.
Chú ý về Đa luồng
Cần lưu ý rằng trình duyệt chủ yếu hoạt động trên một luồng đơn. Điều này có nghĩa là chỉ một luồng chính điều khiển dòng chảy, điều này rất quan trọng khi thảo luận về hiệu suất. Để vượt qua hạn chế này, các trình duyệt sử dụng các cơ chế như vòng lặp sự kiện, rất giống với những gì chúng ta biết từ Node.js.
Điều gì xảy ra khi chúng ta nhập một URL?
Khi một URL được nhập và nhấn Enter, các bước sau diễn ra:
Phân giải Tên miền (DNS)
Bước đầu tiên là chuyển đổi tên miền (ví dụ: google.com) thành địa chỉ IP. Trình duyệt kiểm tra các bộ nhớ cache khác nhau (trình duyệt, hệ điều hành, router, ISP). Nếu không tìm thấy, nó sẽ truy vấn máy chủ DNS, cái trả về địa chỉ IP chính xác.
Kết nối Máy chủ (TCP Handshake)
Sau khi có được IP, trình duyệt thiết lập kết nối bằng cách sử dụng ba bước bắt tay TCP: SYN → SYN-ACK → ACK. Hãy nghĩ đây là tương đương số hóa của việc bắt đầu một cuộc trò chuyện qua điện thoại.
Bảo mật với TLS Handshake
Trước khi trao đổi dữ liệu, một khóa mã hóa an toàn phải được thiết lập. Điều này liên quan đến nhiều lần trao đổi tin nhắn giữa máy khách và máy chủ để đảm bảo rằng lưu lượng không thể bị chặn.
Điện toán Biên & CDN
Để tăng tốc độ này, các công ty như Google, Netflix và Amazon sử dụng CDNs và các vị trí biên trên toàn cầu, giảm độ trễ giữa người dùng và máy chủ.
Phản hồi Đầu tiên – TTFB
Khi mọi thứ đã sẵn sàng, trình duyệt nhận byte đầu tiên (Thời gian đến Byte đầu tiên). Ngay cả trước khi toàn bộ trang được tải, việc hiển thị đã bắt đầu.
Phân tích và Đường dẫn Render quan trọng
Khi trình duyệt nhận HTML ban đầu, nó bắt đầu phân tích. Quá trình này bao gồm:
Xây dựng DOM
HTML được chuyển đổi thành cấu trúc cây gọi là Document Object Model.
Xây dựng CSSOM
CSS cũng được phân tích thành một cây, mô hình CSS. Khác với DOM, nó phải được xây dựng hoàn toàn trước khi tiếp tục.
Cây Render
DOM + CSSOM kết hợp thành Cây Render, chỉ bao gồm các phần tử có thể nhìn thấy với các kiểu đã được tính toán.
Bố trí
Xác định vị trí và kích thước của từng phần tử trên màn hình.
Vẽ
Cuối cùng, các pixel được vẽ trên màn hình. Quy trình này được gọi là Đường dẫn Render quan trọng (CRP). Các script có thể chặn việc xây dựng DOM, và các kiểu có thể làm chậm việc xây dựng CSSOM. Đó là lý do tại sao các kỹ thuật như async và defer cho các script cải thiện tốc độ tải.
Bộ quét Preload
Khi DOM đang được xây dựng, trình duyệt chạy Bộ quét Preload, tìm kiếm các tài nguyên bên ngoài (hình ảnh, script, kiểu) được tham chiếu trong HTML. Những thứ này được tải xuống trước, giảm thời gian render tổng thể.
JavaScript và AST
Khác với các ngôn ngữ biên dịch, JavaScript được thông dịch, nhưng các trình duyệt hiện đại tối ưu hóa việc thực thi:
- Phân tích: Mã được phân tích thành Cây cú pháp trừu tượng (AST).
- Bytecode & JIT: AST được chuyển đổi thành bytecode, được tối ưu hóa bởi trình biên dịch Just-in-Time (JIT), và sau đó được thực thi bởi Máy ảo JS.
- Thực thi: Các script hoãn và chặn chỉ được thực thi khi HTML và CSS đã sẵn sàng, đảm bảo quá trình hiển thị ban đầu được nhanh hơn.
DOM Chi tiết
Một tài liệu HTML có thể được đại diện trong bộ nhớ dưới dạng một cây JavaScript (nút và con). Điều này cho phép các framework, thư viện và các nhà phát triển thao tác trang trong thời gian thực.
Ví dụ:
javascript
const domTree = {
nodeType: 'document',
children: [
{ nodeType: 'doctype', name: 'html' },
{
tagName: 'html',
attributes: { lang: 'vi' },
children: [
{
tagName: 'head',
children: [
{ tagName: 'meta', attributes: { charset: 'utf-8' }, children: [] },
{ tagName: 'meta', attributes: { name: 'viewport', content: 'width=device-width, initial-scale=1' }, children: [] },
{ tagName: 'title', children: [{ type: 'text', content: 'Tiêu đề Trang' }] },
{ tagName: 'link', attributes: { rel: 'stylesheet', href: '/styles.css' }, children: [] },
{ tagName: 'script', attributes: { src: '/scripts/head.js', defer: true }, children: [] }
]
},
{
tagName: 'body',
children: [
{
tagName: 'header',
children: [
{ tagName: 'h1', children: [{ type: 'text', content: 'Tiêu đề Chính' }] },
{
tagName: 'nav',
children: [
{
tagName: 'ul',
children: [
{ tagName: 'li', children: [{ tagName: 'a', attributes: { href: '/' }, children: [{ type: 'text', content: 'Trang Chủ' }] }] },
{ tagName: 'li', children: [{ tagName: 'a', attributes: { href: '/about' }, children: [{ type: 'text', content: 'Về Chúng Tôi' }] }] }
]
}
]
}
]
},
{
tagName: 'main',
children: [
{
tagName: 'article',
attributes: { id: 'post-1' },
children: [
{ tagName: 'h2', children: [{ type: 'text', content: 'Tiêu đề Bài Viết' }] },
{ tagName: 'p', children: [{ type: 'text', content: 'Đoạn văn đầu tiên của bài viết.' }] },
{ tagName: 'img', attributes: { src: '/img/photo.jpg', alt: 'Ảnh' }, children: [] }
]
},
{
tagName: 'section',
attributes: { id: 'features' },
children: [
{ tagName: 'h3', children: [{ type: 'text', content: 'Phần Tính Năng' }] },
{
tagName: 'ul',
children: [
{ tagName: 'li', children: [{ type: 'text', content: 'Tính Năng A' }] },
{ tagName: 'li', children: [{ type: 'text', content: 'Tính Năng B' }] }
]
}
]
}
]
},
{
tagName: 'aside',
children: [
{ tagName: 'h4', children: [{ type: 'text', content: 'Thanh Bên' }] },
{ tagName: 'p', children: [{ type: 'text', content: 'Nội dung hỗ trợ / widget.' }] }
]
},
{
tagName: 'footer',
children: [
{ tagName: 'p', children: [{ type: 'text', content: '© 2025 Công Ty Tôi' }] },
{ tagName: 'ul', children: [{ tagName: 'li', children: [{ tagName: 'a', attributes: { href: '/privacy' }, children: [{ type: 'text', content: 'Chính Sách Bảo Mật' }] }] }] }
]
},
{ tagName: 'script', attributes: { src: '/scripts/bundle.js' }, children: [] }
]
}
]
}
]
};
DOM cũng không phải là tĩnh. Trình duyệt có thể xây dựng nó dần dần trong khi tải xuống các tài nguyên mới. Điều này làm cho DOM trở thành một cấu trúc động và có thể thao tác hoàn toàn thông qua các API JavaScript.
Kết luận
Để hiển thị một trang web duy nhất, trình duyệt phải trải qua một quá trình khổng lồ bao gồm:
- Giải quyết tên miền
- Thực hiện nhiều bước bắt tay
- Xử lý các giao thức bảo mật
- Xây dựng cây DOM và CSSOM
- Tạo Cây Render, bố trí và vẽ
- Thực thi JavaScript đã được tối ưu hóa
Tất cả điều này xảy ra trong một phần của giây. Bài viết này được lấy cảm hứng từ video tuyệt vời của Augusto Galego về cách hoạt động của trình duyệt, nơi anh giải thích các chủ đề này một cách rõ ràng và đơn giản. Tất nhiên, còn nhiều lớp khác liên quan đến chức năng của trình duyệt, và việc xây dựng một cái từ đầu sẽ là một nhiệm vụ rất phức tạp.
Nếu bạn đã đọc đến đây, xin cảm ơn bạn rất nhiều! Mọi phản hồi sẽ được đánh giá cao.