链接即契约:URL 的语义价值
URL 是 Web 的地址系统,也是用户和搜索引擎理解页面内容的入口。一个好的 URL 本身就是说明文档:看到 /products/phones/2026 就知道是 2026 年手机产品列表,看到 ?page=2 就知道是第二页。URL 一旦发布就被收藏、被分享、被收录,改动会造成死链与权重流失,所以 URL 是长期契约,设计时要当 API 来对待。
前端路由让单页应用也能拥有语义化 URL:不刷新页面就能切换视图,同时地址栏、前进后退、分享链接全部保持正常。本文讲清 URL 设计与前端路由的完整链路。
- 语义URL 反映内容层级与含义。
- history无刷新切换并同步地址栏。
- 参数路径与查询参数承载状态。
- 守卫按登录与权限拦截路由。
语义化 URL 设计规范
语义化 URL 有几条硬规范:用小写字母加连字符,单词间用连字符不用下划线;路径反映内容层级,从泛到专;查询参数用于过滤与状态,不用来定位内容;避免无意义编号与动词碎片,能读出来的 URL 才是好 URL。旧链接迁移要做 301 重定向,把权重与用户带到新地址。
# 好:能读出含义
/products/phones/2026
/articles/web/url-routing
# 差:含糊不清
/p?id=1234
/do?action=show&kind=2
URL 长度要克制,深链层级超过四层就要考虑扁平化;中英文混杂要避免;时间与版本这类易变信息放在查询参数里,而不是塞进路径,减少 URL 变更频率。
history 路由与 hash 路由
前端路由有两种模式。hash 模式用 # 号后面的部分做路由,刷新不丢状态、无需服务端配合,但 URL 带 # 不够美观,搜索引擎收录也弱。history 模式用 History API 的 pushState 与 popstate 实现,URL 干净漂亮,但刷新时服务端要配置把未知路径都回退到入口页,否则直接 404。
// history 模式:无刷新切换路由
history.pushState({ page: "detail" }, "", "/products/1");
window.addEventListener("popstate", (e) => {
renderRoute(location.pathname);
});
选型建议:对外的产品站与内容站用 history 模式加服务端回退,SEO 友好;内嵌工具、原型与无法配置服务端的场景用 hash 模式。现代框架路由库都同时支持两种,切换成本不高。
路由状态与参数管理
路由不只是地址,还是应用状态的一部分。路径参数承载主标识,比如 /products/1 里的 1;查询参数承载次要状态,比如筛选条件、页码、排序;这两个来源要跟组件内部状态明确分工,让"刷新后状态还在"成为默认行为。深层状态如果塞进 URL,地址会爆炸,所以要分级:核心状态进 URL,瞬时状态留在内存。
// 读取并同步查询参数
const params = new URLSearchParams(location.search);
const page = Number(params.get("page")) || 1;
// 更新时不丢失其他参数
params.set("page", page + 1);
history.replaceState(null, "", "?" + params.toString());
参数解析要做容错:非法页码回退默认值,缺失参数给合理初始态。pushState 与 replaceState 的区别要分清:push 增加历史记录、可后退,replace 替换当前记录、适合页码这类不希望产生历史堆栈的更新。
路由守卫与懒加载
很多页面有访问条件:未登录跳登录页,无权限提示 403。路由守卫把这类逻辑集中处理,比每个页面自己判断更可控。守卫的典型流程:解析目标路由与当前用户态,判断放行、重定向还是阻断,并记录来源路径,登录成功后回跳。
// 守卫示意:未登录重定向
function guard(to) {
if (to.meta.requiresAuth && !isLoggedIn()) {
return { redirect: "/login", query: { from: to.path } };
}
return true;
}
懒加载与路由配合是性能利器:按路由切分代码,访问到哪个页面才加载哪块 JS。首屏只加载首屏需要的模块,其余页面代码按需拉取,配合预取能明显加快首屏与后续导航。
链接可分享性与深链
一个好链接应该可收藏、可分享、可被搜索引擎收录。分享出去的链接要能还原完整状态:页面内容、筛选条件、当前定位全部带在 URL 里;分享后打开,用户看到的不该是空白默认页。深链(Deep Link)对移动端同样重要:扫码或点击分享链接,应直达内容页而非首页。
// 分享时把状态写进 URL
const shareUrl = location.origin + location.pathname +
"?" + params.toString();
navigator.clipboard.writeText(shareUrl);
可分享性还要求 URL 稳定:内容换标题不改路径,分页保持旧链接可访问,删除的内容做 410 或软提示。为分享页面配上合适的 meta 标题与摘要,让链接在社交平台展开时自带卡片。
落地清单
- URL 小写加连字符,路径反映内容层级,可读可猜。
- 旧链接变更做 301,避免死链与权重流失。
- 对外内容站用 history 模式并配服务端回退。
- 核心状态进 URL,瞬时状态留内存,参数解析做容错。
- 登录与权限集中用路由守卫,来源路径记录与回跳。
- 按路由懒加载代码,分享链接保证可还原完整状态。