系统设计 / 路由设计 · 稳定入口与可变目标 待审核 23 页

路由设计 · 稳定入口与可变目标

「路由」远不止浏览器地址栏里那条 URL。它是一层可控的 indirection —— 对外暴露一个稳定入口 (一条 URL、一个服务端 route、一个 App 里看不见的 deep link、一条短链),入口背后的目标则随时可换。正因为多了这一层,才能统一改址、按渠道统计、灰度上线,而那些已经发出去的入口一个字都不用动。拆开这一层,里面是两半:一张规则表决定入口该去哪 (rule,见匹配引擎),再用某种机制把请求真正送过去 (dispatch —— reverse proxy / redirect / rewrite / 系统级路由)。下面分五组,从「为什么需要它」讲起,沿服务端 → 浏览器客户端 → 基础设施与前端深层逐层展开,每页都能改输入、即时看到路由跑出的结果。

路由是什么 · 为什么需要这一层

先把「稳定入口 ↔ 可变目标」的心智模型立起来,再看它最日常的一个实例 —— 短链为什么会存在。

服务端路由 · 一张表 + 一次 dispatch

最经典的路由都在服务端:一张表把 path 匹配到 handler,命中后用 3xx 或 rewrite 把请求送达。先拆匹配引擎重定向语义两块内核,再落到三个真实场景 —— 分享统一入口、App deep link、远程配置入口。

客户端 / 浏览器路由 · 同一套模型搬进浏览器

把这一层搬进浏览器 —— 单页应用 (SPA) 不刷新整页、自己维护一份「当前 location」与 history 栈,共用同一套「稳定入口 ↔ 可变目标」模型,只是状态从服务端挪到客户端。末两页是浏览器为路由新做的原生接口。阅读前建议先了解 URL 的组成 (scheme/host/path/query/fragment),可参考 URL Anatomy 系列

基础设施层的 dispatch · 那台 matcher 搬到最前层

把匹配引擎搬到服务端最前面那一层,决定一条请求转发给谁 (网关按 = > 最长 prefix > regex 择优)、按什么比例 (加权流量切分,canary / 灰度 / 蓝绿的底座)、按哪个维度 (多租户用 host 而非 path)。

前端更深的路由机制 · 超出「平表匹配」的进阶

回到前端,几个超出「一条 path 命中一条路由」的机制:一条 path 命中一整条嵌套链 (layout 层层复用)、一条 URL 同时填多个槽 (并行 / 拦截路由)、locale 写在前缀或域名的国际化路由、把无数等价写法收敛到唯一地址的 URL 规范化

嵌套路由与 layout

平表里一条 path 命中一条路由;真实前端的路由是一棵树,一条 path 命中从根到叶的一整条链。切换兄弟页面时共享的外层 layout 原地不动,这是平表给不了的性质。

并行路由与拦截路由

一个 layout 有多个具名 slot,一条 URL 同时把它们填好。配上拦截路由,同一个地址在软导航下浮成模态、在硬导航下渲染整页。

移动端页面栈与模态栈

「从 B 退回 A 时 A 原封不动」的关键只有一条:A 从来没被销毁过。声明式引擎把 URL 翻译成期望栈,与实际挂载做 diff,公共前缀保活、多出来的销毁、缺的补上。

移动端页面栈 · 整页版

页面栈引擎的整页版:push 保活、pop 即销毁、设置模态从底部滑入、左缘侧滑返回、直达深层地址时合成背景。与讲解版同一套 reconcile。

页面栈 · vue-router

页面栈的 vue-router 实现

同一套页面栈 scheme 落到 vue-router 的现成原语上:meta.layer 定方向、<transition> 分进退、<keep-alive :include> 白名单即「还活着的栈」。

vue-router · keep-alive · transition

页面栈 · vue-router 整页版

同一套页面栈 scheme 收敛成整视口的一台 App,四条 vue-router 原语各管一段:meta.layer<transition><keep-alive :include>beforeEach 守卫。

国际化路由

locale 写进 URL 才可分享、对 SEO 友好,路径前缀与域名是两种写法。URL 不带 locale 时读 Accept-Language 协商:一串带 q 权重的偏好加 fallback 链。

URL 规范化与 canonical 地址

同一篇文章可被无数写法不同却等价的 URL 指向。规范化用一条幂等的变换流水线把它们收敛到唯一 canonical 地址,擦完不一样就回 301。

🔗 相关链接

  • MDN · HTTP Redirections developer.mozilla.org 对应「重定向」页:301/302/303/307/308 各自语义、永久 vs 临时、方法保持的逐项说明与典型用途。
  • RFC 7231 §6.4 · Redirection 3xx IETF 权威定义:为什么 307/308 是「方法明确版」的 302/301,以及 303 强制改 GET 的规范出处。
  • React Router · Routing reactrouter.com 对应「匹配引擎」页:前端路由的 dynamic segment :id、splat *、嵌套与最具体者优先的真实实现。
  • Apple · Universal Links developer.apple.com 对应「deep link」页:iOS 用 apple-app-site-association 把 https 链接路由进 App 的官方机制 (Android 侧对应 App Links + assetlinks.json)。
  • OWASP · Unvalidated Redirects and Forwards owasp.org 对应「分享入口」页的安全小节:open redirect 的危害与 allowlist 防御 —— 跳转目标为什么必须校验。
  • MDN · History API developer.mozilla.org 对应「客户端路由」「history 模式」两页:pushState / replaceState / go / back / forward 与 popstate 事件的底层动作。
  • MDN · Navigation API developer.mozilla.org 对应「Navigation API」页:navigate 事件、intercept、entries()、currententrychange —— 为客户端路由重做的新一代接口。
  • MDN · URLPattern API developer.mozilla.org 对应「URLPattern」页:构造器、test() / exec()、整条 URL 各组件的 pattern 语法与命名组提取 —— 浏览器内建的 URL matcher。
  • nginx · location 指令 nginx.org 对应「反向代理 / API 网关」页:= / ^~ / ~ / 普通前缀四种 location 的匹配优先级与求值顺序 —— 那个「正则反超前缀」的陷阱出处。
  • Istio · Traffic Management istio.io 对应「加权流量切分」页:service mesh 怎样用 VirtualService / DestinationRule 按 weight 切 subset、按 header 定向 —— canary / 灰度的生产实现。
  • Next.js · Parallel & Intercepting Routes nextjs.org 对应「并行 / 拦截路由」页:@slot 具名槽、(.) / (..) / (...) 拦截约定、default.js 兜底的官方模型。