路由与页面状态
地址栏不是只用来跳页面的。它是用户可以刷新、复制、收藏和前进后退的状态容器。把重要页面状态放进 URL,页面才具备可恢复性。
三类状态放在不同位置
| 状态 | 推荐位置 | 示例 |
|---|---|---|
| 资源身份 | 路径参数 | /members/42 |
| 筛选、分页、排序 | 查询参数 | ?status=active&page=2 |
| 临时交互细节 | 组件内部 | 输入框聚焦、菜单展开 |
将弹窗是否打开、草稿内容等临时细节写入 URL,通常会让链接难以理解;把当前筛选条件只留在内存中,则用户刷新后会丢失上下文。
参数是外部输入,需要转换和校验
路由参数和查询参数本质上是字符串。读取后先给默认值、转换类型并限制合法范围。
js
function parsePage(value) {
const page = Number(value)
return Number.isInteger(page) && page > 0 ? page : 1
}
const page = parsePage(route.query.page)
const status = ['active', 'archived'].includes(route.query.status)
? route.query.status
: 'active'这样可以避免 NaN、超大页码或非法状态直接流进接口请求。
导航要反映用户意图
用户主动改变筛选、打开详情时,使用会新增历史记录的导航;仅纠正默认参数、同步内部状态时,使用替换式导航,避免“点一次筛选要按很多次返回”。
js
router.replace({
query: { ...route.query, page: 1, status: nextStatus }
})页面切换时处理异步结果
当路由参数变化时,旧页面请求可能晚于新页面返回。监听参数变化后取消旧请求或只接受最新结果,避免用户在 A 页面看到 B 的数据。
- 参数变化时重置加载、错误和空状态。
- 组件卸载时清理订阅、定时器与未完成请求。
- 无权限、资源不存在和网络失败要进入不同的可理解页面状态。
常见错误:详情页组件复用,参数变了数据不刷新
这是 Vue Router 里排名第一的坑。/user/:id 详情页,从用户 1 跳到用户 2,页面还显示用户 1:
js
// 只在挂载时加载一次
onMounted(() => {
loadUser(route.params.id)
})症状:URL 明明从 /user/1 变成了 /user/2,页面内容却没变,还是用户 1 的数据。刷新一下才正常。用户最容易在"列表 → 详情 → 返回 → 另一个详情"这条路径上遇到。
为什么错:两个路由用的是同一个组件,Vue 出于性能会复用组件实例——onMounted 只在第一次触发,后续参数变化不会再 mount。route.params.id 变了,但没人通知组件重新加载。
正确做法:监听参数变化重新加载,或用 key 强制重建组件:
js
// 方案一:监听参数
watch(() => route.params.id, (id) => {
loadUser(id)
}, { immediate: true })
// 方案二:强制重建(简单粗暴,适合详情页)
<router-view :key="$route.fullPath" />记住:"同组件不同参数"不会重新挂载。详情页加载数据,要么 watch 参数,要么靠 key 重建。
路由设计得好,页面不仅能跳转,还能被可靠地恢复和分享。