浏览器导航与前端路由

从浏览器导航、会话历史、History API 到前端路由器,梳理 URL 变化如何驱动网络请求、应用状态和视图渲染。

#type / synthesis #status / growing #tech / dev / frontend #platform / browser #resource / web

[!info] related notes

浏览器导航与前端路由

范围

这篇笔记关心的不是“某个路由库 API 怎么用”,而是更底层的问题:

  • URL 变化时,浏览器会做什么
  • 哪些变化会触发网络请求,哪些不会
  • 前端路由器到底接管了浏览器哪一部分工作

为什么要放在一起理解

“路由”这个词很容易把三层东西混在一起:

  • 浏览器导航机制
  • 前端应用里的客户端路由
  • 服务端对 URL 的资源分发

如果不先分层,后面讲 hashchangepushState()router.push()、服务端 fallback 时就很容易串线。

先抓住总问题

当 URL 变化时,系统要回答四件事:

  1. 要不要请求服务器
  2. 历史记录怎么变
  3. 当前应用状态怎么同步
  4. 最终该渲染什么内容

路由本质上就是这四件事的协作机制。

浏览器导航在做什么

浏览器层的“导航”不只包括点击链接,还包括:

  • 地址栏输入 URL
  • 点击 <a href>
  • 表单提交
  • location.assign() / location.replace()
  • 前进 / 后退
  • 刷新

从结果看,可以粗分成两类:

1. 会触发新文档加载的导航

典型表现:

  • 浏览器发起新的 HTTP 请求
  • 新 HTML 返回
  • 旧文档被卸载
  • 新页面重新解析和执行

这类更接近传统 MPA 导航。

2. 不触发整页刷新的同文档导航

典型表现:

  • 地址变了
  • 历史记录可能变化
  • 但当前文档并没有被整体替换

常见来源:

  • hash 变化
  • history.pushState()
  • history.replaceState()

这类能力给 SPA 留出了接管空间。

会话历史是底层骨架

浏览器会维护当前 tab 的 session history

它决定了:

  • 前进 / 后退能回到哪里
  • 当前历史条目关联什么 URL
  • 某些条目是否附带 history.state

history 对象只是这套机制暴露给脚本的接口。见 History 对象

前端路由器到底接管了什么

SPA 不是浏览器天然就会的能力,而是前端代码主动接管了默认导航流程。

典型过程是:

  1. 用户点击链接
  2. 前端阻止浏览器默认整页跳转
  3. 前端调用 pushState() 或改 hash
  4. 路由器解析 URL
  5. 路由器匹配目标视图
  6. 执行守卫、数据获取、懒加载
  7. 更新当前 UI

所以前端路由不是“浏览器帮你切页面”,而是“前端借用浏览器的 URL 和历史能力,在同一份文档里组织页面系统”。

Hash 路由和 History 路由的边界

Hash 路由

  • URL 中 # 后面的 fragment 不会发给服务器
  • 适合静态托管或部署回退不方便的场景
  • 地址可见,但路径语义较弱

History 路由

  • 路径更自然,例如 /users/123
  • 基于 pushState() / replaceState() / popstate
  • 需要服务端把未知前端路径回退到 index.html

两者都是客户端路由方案,区别不在“谁能切页面”,而在“URL 形态”和“是否需要服务端协作”。

为什么服务端 fallback 必不可少

在 History 路由下,如果用户直接访问 /users/123,浏览器会真的去请求这个路径。

如果服务端只认真实文件或真实页面资源,而没有把未知前端路径回退给 SPA 入口页,结果通常就是 404。

所以服务端 fallback 的本质是:

服务器把“这个路径该展示哪个前端页面”的决定权交回给前端路由器。

URL 不只是地址,也是可序列化状态

路由系统的价值不只是让页面“能切换”,还在于让一部分应用状态变得可分享、可恢复、可追踪。

适合编码进 URL 的常见状态:

  • id
  • page
  • sort
  • keyword
  • tab

不适合直接放进 URL 的常见状态:

  • 临时弹窗开关
  • hover 状态
  • 大体积对象
  • 敏感 token

所以从系统视角看,路由本质上也是一种状态分层机制。

路由和页面运行时状态的关系

前端应用里有些状态应该进入 URL,有些只应该留在内存里。

  • URL 状态负责分享、恢复和前进后退
  • 页面运行时状态负责当前交互现场

页面运行时状态

如果一份状态只存在运行时内存里,那么:

  • SPA 内部切换时也许还能保住
  • 整页刷新或新文档导航时通常会丢

前进后退为什么经常比想象中复杂

因为浏览器恢复的不只是 URL,还可能涉及:

  • history.state
  • 滚动位置
  • 表单值
  • BFCache 快照恢复

所以“回到上一个页面”并不总等于“重新跑一遍初始化逻辑”。

和框架路由的关系

Vue Router、React Router、Next.js App Router 这些框架路由,本质上都是在浏览器原生导航机制之上,再补上:

  • 路由匹配
  • 参数提取
  • 守卫
  • 懒加载
  • 数据获取
  • 布局嵌套

浏览器负责 URL 与历史;框架负责把这些底层能力组织成应用级页面系统。

最短记忆方式

浏览器导航负责处理 URL、历史和文档切换;前端路由负责在这些能力之上,把 URL 进一步映射成应用状态和界面结构。

创建于 2026/4/23 更新于 2026/5/27