H5 前端路由设计与页面守卫
H5 前端路由设计与页面守卫
路由不只是把地址映射到组件。它承载了页面状态、返回行为、参数解析、权限控制和分享链路。前端路由设计得好,用户刷新、回退和直达链接都能得到一致体验;设计得不好,容易出现页面丢状态、返回异常和权限绕过。
一、路由是业务状态的一部分
筛选条件、分页、选中标签等信息,如果只存在组件内部状态里,用户刷新后就会丢失。把这些信息放进路由查询参数,可以让页面状态可恢复、可分享。
type ProductQuery = {
page: number;
category: string;
};
路由中的状态应保持简单。只在 URL 中保存业务上有意义、用户可能直接进入的条件,不要把复杂对象随意序列化进去。
二、路径参数与查询参数的分工
路径参数用于资源的唯一标识,查询参数用于影响展示的可变条件。
/order/123/detail
/order/list?status=pending&page=2
订单编号是资源标识,适合放在路径参数中。状态筛选和页码是查询条件,适合放在查询参数中。两者混用会让页面入口不清晰,也不利于埋点和分享。
三、参数解析要收敛到边界
路由查询参数来源于 URL 字符串,本质上是弱类型数据。应在路由边界统一解析并转换为业务类型,而不是让每个组件都自己去读字符串。
function parseProductQuery(
query: Record<string, string | string[] | undefined>
): ProductQuery {
const rawPage = Array.isArray(query.page)
? query.page[0]
: query.page;
const page = Number(rawPage);
return {
page: Number.isFinite(page) && page > 0
? Math.floor(page)
: 1,
category: typeof query.category === 'string'
? query.category
: ''
};
}
解析后的对象进入组件时已经是明确的业务类型,组件内部不再关心它来自 URL 还是缓存。
四、路由变更与组件复用
从列表页进入详情页,或从详情页切换到另一个详情页,路由组件可能被复用而不是销毁重建。如果只在组件创建时读取参数,后续切换不会触发更新。
watch(
() => route.params.orderId,
(orderId) => {
loadOrder(orderId);
}
);
组件应监听会变化的路径参数,并在切换时先取消旧请求,再发起新请求。否则慢请求可能晚于新请求返回,覆盖新数据。
五、页面守卫的职责
页面守卫适合做“进入前必须满足”的条件判断,例如登录态、角色权限、业务前置条件。
async function beforeEachGuard(to: RouteLocation): Promise<boolean> {
if (to.meta.requiresLogin && !authStore.isLogged) {
authStore.setRedirectAfterLogin(to.fullPath);
return false;
}
return true;
}
守卫中应尽量避免复杂业务逻辑。复杂的页面初始化放在页面组件内部,守卫只负责拦截和跳转。
六、登录重定向
用户未登录访问受限页面时,应记录原始目标,登录成功后再回到该页面。目标地址应保持完整路径,包括查询参数。
function redirectAfterLogin(): void {
const target = authStore.redirectAfterLogin || '/home';
authStore.clearRedirectAfterLogin();
router.replace(target);
}
如果记录了外部跳转目标,跳转前必须校验地址合法性,只允许应用内路径,避免被构造出恶意跳转。
七、返回行为与导航模式
页面上点击返回按钮和浏览器返回按钮的行为应一致。业务上的“返回”通常使用历史回退,而不是硬编码跳转到固定页面。
function goBack(): void {
if (window.history.length > 1) {
router.back();
} else {
router.replace('/home');
}
}
对于需要替换当前历史的场景,例如登录成功后不应回退到登录页,使用 replace 而不是普通跳转。
八、滚动位置与返回恢复
用户从详情页返回列表页时,通常期望回到原来的滚动位置。这需要路由框架的滚动恢复能力,同时列表状态在返回后仍然可用。
const savedScrollTop = scrollHistory.get(routeKey);
if (savedScrollTop !== undefined) {
container.scrollTop = savedScrollTop;
}
滚动位置保存和恢复要与路由切换时机匹配。如果组件尚未渲染完成就恢复滚动,位置会被重置。应在列表数据渲染后再恢复。
九、路由与埋点
路由变化是页面级埋点的天然时机。埋点参数应来自路由和解析后的业务参数,而不是直接读取散落在各处的组件状态。
router.afterEach((to) => {
tracker.pageView(to.path, to.query);
});
路径中的个人敏感信息应避免进入埋点系统。页面地址要经过统一清洗后再上报。
十、错误路由兜底
用户可能输入错误地址,或点击已经失效的链接。路由表应提供明确的未匹配页面,引导用户回到可用入口。
{
path: '/:pathMatch(.*)*',
component: NotFoundPage
}
页面加载失败也应给予用户明确的提示和操作,而不是停留在空白页面或无限加载状态。
总结
前端路由设计的核心是把页面状态显式化。路径参数表达资源,查询参数表达条件,边界统一解析并转换为业务类型。组件要正确处理路由复用,守卫只负责拦截,复杂初始化留在组件内。登录重定向、返回恢复、滚动位置和错误页面共同决定了真实用户对页面流畅性的感受。
- 点赞
- 收藏
- 关注作者
评论(0)