路由
路由决定了页面级拆分、数据边界与加载策略,也是性能与可维护性的关键切点。
路由落地清单
这个清单看起来很“朴素”,但做扎实后,大部分路由相关体验问题会自然消失。
- 为路由段提供 Loading UI(骨架屏优先,必要时再转圈)。
- 为路由段提供 Error boundary,并给出可操作的恢复路径(重试/返回)。
- 按“意图”预取(`hover`/视口内),避免全站无差别预取。
- 支持滚动恢复,或明确导航后的滚动行为(例如进入新页滚到顶部)。
路由就是边界(UI、数据、错误)
一个好的路由边界要回答:这里加载哪些数据、哪些可以流式返回、错误在哪里兜底、哪些 UI 应该在导航中保持稳定。
当你把路由当成边界后,“在哪里取数/在哪里缓存”就不再是个人偏好,而是架构决策。
- 把导航/布局/侧边栏这类公共壳放在更外层路由,避免每次切页都 `remount`。
- 数据加载上移到“足以避免瀑布”的高度,但不要再高。
- 错误边界尽量贴近失败单元,恢复动作才具体(重试/返回上级)。
加载策略
Loading UI 不只是装饰,它决定了用户是否把“抖动/丢状态”感知为 bug。
- 骨架屏优先,并尽量匹配最终布局,减少跳动。
- 局部加载别用全屏转圈,尽量让页面其他部分仍可交互。
- 明确导航时是“保持旧屏并乐观过渡”,还是“立刻清空并严格等待”。
滚动恢复(最小模式)
如果框架没有自动恢复滚动(或你用了自定义滚动容器),可以按路由 `key` 记录并恢复滚动位置。
TypeScript
import { useEffect } from 'react';
export function useScrollRestoration(key: string) {
useEffect(() => {
const saved = sessionStorage.getItem(`scroll:${key}`);
if (saved) window.scrollTo({ top: Number(saved) });
const onScroll = () => {
sessionStorage.setItem(`scroll:${key}`, String(window.scrollY));
};
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, [key]);
}避免瀑布请求
如果多个组件在挂载时各自请求,常会形成瀑布。把数据获取上移到路由边界或集中数据层,可以更好地并发与缓存。
常见坑
- 路由切换时状态丢失/闪烁,缺少骨架屏与错误边界。
- 把数据获取散落在多个组件里,导致瀑布请求与重复 fetch。
示例:稳定的 URL 状态
当某些视图状态需要可分享/可书签时,把它建模到 URL 会更稳健。
TypeScript
import { useMemo } from 'react';
export function useQueryParam(searchParams: URLSearchParams, key: string) {
return useMemo(() => searchParams.get(key) ?? '', [searchParams, key]);
}上线检查清单
- 每个路由都有 `loading`/`error`/`empty` 三态。
- 导航中该保留的状态保留(layout),该重置的重置(leaf)。
- 滚动行为是“有设计的”:该恢复就恢复,该重置就重置。
- 预取按意图触发,不全站滥用;不会压垮网络。
延伸阅读
- Guides:数据获取