How Browsers Work · Part 21 — Process Model Deep: Site Isolation, BFCache & Prerender
Mô hình đa tiến trình ở mức sâu: Site Isolation và OOPIF, Back/Forward Cache, prerender và Speculation Rules, cùng Page Lifecycle API quản lý tài nguyên giữa các tab.
Phần 1 và Phần 11 giới thiệu kiến trúc đa tiến trình. Phần này đi sâu vào ba cơ chế hiện đại: Site Isolation dùng renderer-process boundary giữa site; BFCache giữ page sống nhưng tạm dừng để back/forward nhanh; và prerender chuẩn bị navigation có xác suất cao. Cả ba đều mạnh, nhưng đều có eligibility, giới hạn tài nguyên và khác biệt giữa browser.
1. Site Isolation — process boundary giữa site
Trên desktop, Chrome cố giới hạn mỗi renderer process chỉ chứa document từ một site; iframe cross-site thường chạy thành OOPIF. Process có thể được reuse cho document cùng site, chính sách trên Android/tài nguyên thấp khác desktop, nên đây không phải phép đếm “mỗi domain đúng một process”.
Trang a.com nhúng iframe b.com:
┌─ Renderer process (a.com) ─┐ ┌─ Renderer process (b.com) ─┐
│ document a.com │ │ iframe b.com (OOPIF) │
└────────────────────────────┘ └────────────────────────────┘
cùng hiển thị trên một tab, nhưng bộ nhớ tách biệt
Iframe khác site chạy trong process riêng gọi là OOPIF (Out-of-Process Iframe). “Site” hiện là schemeful site — thường gồm scheme + registrable domain (ví dụ https://example.com) — không phải full origin. Hai origin cùng site như https://a.example.com và https://b.example.com vẫn có thể chia process; Site Isolation không thay Same-Origin Policy và không hứa tách mọi cặp cross-origin. Cross-origin isolation (Phần 20) là opt-in policy khác, bổ sung cho ranh giới process này.
2. BFCache — đóng băng cả trang
Back/Forward Cache giữ page đang sống nhưng bị pause trong bộ nhớ khi bạn điều hướng đi: DOM, JS heap và phần lớn state browser được bảo toàn; JS task/timer không chạy khi frozen. Đây không phải HTTP cache và không phải serialise toàn bộ page ra một file. Bấm Back → browser có thể restore gần như tức thì thay vì load lại.
Rời trang A → B:
Không BFCache: A bị huỷ. Back → tải A lại từ mạng (chậm).
Có BFCache: A bị "đóng băng" trong bộ nhớ. Back → rã đông, hiện ngay (~tức thì).
Vào/ra BFCache phát sự kiện pageshow/pagehide với cờ persisted:
window.addEventListener('pageshow', (e) => {
if (e.persisted) refreshTimestamp(); // trang vừa khôi phục từ BFCache → làm tươi dữ liệu
});
Eligibility thay đổi theo browser và phiên bản; đừng duy trì một blacklist thuộc lòng. Những điểm thực dụng:
- Listener
unloadvẫn chặn BFCache trong một số browser/config và bản thân event không đáng tin — bỏ nó. - iframe hoặc API state trong bất kỳ frame nào cũng có thể làm cả page không eligible/ bị evict.
Cache-Control: no-storevẫn là rào cản portability. Từ 2025 Chrome cho nhiều trangno-storevào BFCache khi đánh giá an toàn, nhưng evict khi cookie/auth thay đổi và áp hạn chế thêm cho WebSocket/WebTransport/WebRTC; browser khác có thể vẫn chặn.- Test bằng DevTools Application → Back/forward cache. Trong field,
PerformanceNavigationTiming.notRestoredReasonscó thể cung cấp reason tree trên browser hỗ trợ;pageshow.persistedmới là tín hiệu restore trực tiếp.
Đừng dùng
unload. Dùngvisibilitychangeđể lưu state sớm,pagehidecho logic tương thích history navigation, vàpageshowđể làm tươi dữ liệu khi restore.
3. Page Lifecycle — trình duyệt quản lý tài nguyên tab
Trình duyệt chủ động đóng băng và thậm chí loại bỏ (discard) tab nền để tiết kiệm CPU/RAM. Page Lifecycle API cho trang biết trạng thái của mình:
ACTIVE ──► PASSIVE ──► HIDDEN ──► FROZEN ──► (TERMINATED / DISCARDED)
(đang dùng) (tab nền) (CPU bị dừng)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
saveStateNow(); // lưu ngay — có thể bị freeze/discard sau đó
navigator.sendBeacon('/analytics', JSON.stringify(pendingAnalytics));
}
});
// freeze/resume là lifecycle hook của Chromium; vẫn cần fallback chuẩn ở trên
document.addEventListener('freeze', releaseResources);
document.addEventListener('resume', reconnect);
if (document.wasDiscarded) restoreAfterDiscard(); // page đã reload sau discard
Quy tắc vàng: coi visibilitychange → hidden là cơ hội quan sát cuối đáng tin cậy nhất, vì discard không phát event tại thời điểm xảy ra và thứ tự lifecycle khác nhau giữa browser. document.wasDiscarded chỉ cho page mới biết lần load này theo sau một discard.
4. Prerender & Speculation Rules — dựng trước trang sau
Chromium hiện đại có thể prerender một trang bạn có thể sắp vào trong context ẩn. Nếu speculation hoàn tất và navigation đủ điều kiện activation, trang có thể hiện gần như tức thì. Điều khiển bằng Speculation Rules:
<script type="speculationrules">
{
"prerender": [{
"where": { "href_matches": "/product/*" },
"eagerness": "moderate"
}],
"prefetch": [{
"where": { "selector_matches": "a.next" },
"eagerness": "eager"
}]
}
</script>
- prefetch: chuẩn bị response cho một document navigation nhưng chưa render/execute page.
- prerender: load và render page trong context ẩn, với một số API bị defer/hạn chế tới activation — mạnh nhưng tốn memory/CPU/network.
eagerness:immediate/eager/moderate/conservative. Chỉimmediatecó nghĩa ổn định là “càng sớm càng tốt”; heuristic của ba mức còn lại có thể đổi theo browser/version.
Trang được prerender trong browser hỗ trợ có document.prerendering === true; hoãn side effect không cần thiết (analytics, ad impression, autoplay) tới activation:
if ('prerendering' in document && document.prerendering) {
document.addEventListener('prerenderingchange', () => sendAnalytics(), { once: true });
} else {
sendAnalytics(); // đã là điều hướng thật
}
5. Hệ quả thực tế cho developer
Bảo mật: Site Isolation/OOPIF → cross-site document tách renderer process khi policy áp dụng.
Back/Fwd: giữ trang đủ điều kiện BFCache → bỏ 'unload', test reason thay vì đoán blacklist.
Tab nền: xử lý visibilitychange/freeze/resume → lưu sớm, nhả tài nguyên.
Điều hướng: Speculation Rules để prefetch/prerender → navigation sau có thể gần tức thì.
Đo lường: phân biệt prerender vs điều hướng thật để analytics không sai lệch.
Tất cả đều xây trên một ý tưởng: trình duyệt quản lý nhiều trang ở nhiều giai đoạn sống cùng lúc, và cho web app các hook để hợp tác.
6. Tóm tắt
- Site Isolation dùng renderer-process boundary cho cross-site document; OOPIF không đồng nghĩa mọi cross-origin đều có process riêng.
- BFCache pause page sống (DOM + heap) để back/forward nhanh; nghe
pageshow.persisted, bỏunload, và dùng DevTools/notRestoredReasonsđể tìm blocker. - Page Lifecycle: coi
visibilitychange→hiddenlà cơ hội đáng tin cậy nhất để lưu;freeze/resumelà hook Chromium và discard chỉ quan sát được sau reload quadocument.wasDiscarded. - Speculation Rules cho document prefetch/prerender;
eagernesscân tốc độ với lãng phí, còn heuristic cụ thể có thể đổi. - Hoãn side-effect khi
document.prerenderingcho tớiprerenderingchangeđể không làm sai analytics.
Phần tiếp theo: Capstone 2 — ghép mọi tầng nội bộ lại bằng profiling end-to-end: đọc Performance panel/Perfetto và khoanh một jank về process/thread/giai đoạn có bằng chứng mạnh nhất.