跳转到主要内容

路由

路由决定了页面级拆分、数据边界与加载策略,也是性能与可维护性的关键切点。

路由落地清单

这个清单看起来很“朴素”,但做扎实后,大部分路由相关体验问题会自然消失。

  • 为路由段提供 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 - React 文档 - React 文档