浏览器前进后退的底层实现
一、核心数据结构:双栈模型
浏览器的历史导航通常采用两个栈(stack)来实现:
- 后退栈(Back Stack) :保存从当前页面可以“后退”的历史页面。
- 前进栈(Forward Stack) :保存从当前页面可以“前进”的页面,仅在用户执行“后退”后才有内容。
这种双栈结构可以很好地模拟用户的导航行为。
1.1 页面访问流程
当用户从页面 A 访问到页面 B 时:
- 当前页面 A 被压入 后退栈
- 前进栈被清空
- 当前页面变为 B
状态如下:
Back Stack: [A]
Forward Stack: []
Current Page: B
1.2 后退操作
点击“后退”按钮时:
- 当前页面 B 被压入 前进栈
- 后退栈弹出页面 A 作为当前页面
状态更新:
Back Stack: []
Forward Stack: [B]
Current Page: A
1.3 前进操作
点击“前进”按钮时:
- 当前页面 A 被压入 后退栈
- 前进栈弹出页面 B 作为当前页面
状态更新:
Back Stack: [A]
Forward Stack: []
Current Page: B
二、浏览器中的 window.history
对象
浏览器提供了 window.history
对象,以编程方式访问和操作浏览历史记录。常用的方法有:
方法 | 功能说明 |
---|---|
history.back() |
等同于点击“后退”按钮 |
history.forward() |
等同于点击“前进”按钮 |
history.go(n) |
跳转到相对位置的历史记录,n 可正可负 |
history.pushState() |
添加一条历史记录(不会刷新页面) |
history.replaceState() |
替换当前历史记录 |
这套 API 在单页应用(SPA)中尤为重要,它让开发者可以模拟浏览器的原生导航行为,同时避免页面刷新。
三、现代浏览器的内部机制
3.1 NavigationController
现代浏览器会使用 NavigationController
来管理会话历史。这个控制器负责:
- 记录历史记录条目(SessionHistoryEntry)
- 控制页面导航方向
- 管理与渲染进程之间的切换(跨进程导航)
3.2 SessionHistoryEntry
每条历史记录不仅包含页面 URL,还记录:
- 页面状态(滚动位置、表单数据等)
- 页面是否为跨域页面
- 页面来源(是用户点击链接跳转,还是脚本跳转)
这样,即便用户返回到很久之前的页面,浏览器也能尽量还原页面原貌。
3.3 跨进程导航
如果用户在多站点间切换(如从 example.com
到 google.com
),出于安全性考虑,浏览器会将页面分配到不同的渲染进程。此时,后退或前进操作会触发“跨进程导航”,需要更复杂的调度机制。
四、模拟实现
const backStack = [];
const forwardStack = [];
let currentPage = "A";
function visit(page) {
backStack.push(currentPage);
currentPage = page;
forwardStack.length = 0;
console.log("Visit:", currentPage);
}
function back() {
if (backStack.length > 0) {
forwardStack.push(currentPage);
currentPage = backStack.pop();
console.log("Back to:", currentPage);
}
}
function forward() {
if (forwardStack.length > 0) {
backStack.push(currentPage);
currentPage = forwardStack.pop();
console.log("Forward to:", currentPage);
}
}
// 示例
visit("B"); // 当前 A -> B
visit("C"); // 当前 B -> C
back(); // 当前 C -> B
back(); // 当前 B -> A
forward(); // 当前 A -> B
五、单页应用中的前进后退
在传统页面中,浏览器自动处理历史记录。但在单页应用(SPA)中,页面不会刷新,所有状态由 JavaScript 管理。这就需要使用 pushState
和 popstate
来手动控制导航行为:
// 添加新的历史记录
history.pushState({ page: 2 }, "Title 2", "/page2");
// 监听用户点击“后退”按钮的行为
window.addEventListener("popstate", function (event) {
console.log("User navigated to:", event.state);
});
结语✒️
知识点+1✨✨✨