Notes

Next.js App Router:Segments / Layouts / Loading-Error-NotFound / Prefetch(落地篇)

一句话结论:在 App Router 体系下,路由不是“匹配 URL → 渲染组件”这么简单,而是用 segment tree(分段树)组织 UI 与数据边界layout/page 决定可复用的壳,loading/error/not-found 决定“等/错/无”的兜底策略;当你把这些边界与 Next 的 prefetch + fetch 缓存/重验证 一起设计,才能避免“切页白屏、局部闪烁、线上不更新”等生产事故。

0. 先把 App Router 当成“树”来看

App Router 的关键不是某个 API,而是它的组织方式:

  • URL path 会映射到 app/ 目录下的一棵 segment tree
  • 每个 segment 可以定义:
    • layout.tsx:这一段及其子树共享的 UI 壳(导航栏、侧边栏、权限壳)
    • page.tsx:这一段的页面入口
    • loading.tsx:这段树在加载时的 fallback(通常是 skeleton)
    • error.tsx:这段树的错误边界(Client Component)
    • not-found.tsx:这段树的“资源不存在”边界

你可以把它理解为:路由 = UI 边界树 + 数据边界树

1. Segments:用“分段”表达信息架构

1.1 什么是 segment?

  • app/dashboard/settings/page.tsx 中的 dashboardsettings 都是 segment
  • 动态段:[id][...slug][[...slug]]

它承载两个含义:

  1. URL 的语义结构(信息架构)
  2. UI 与边界的复用层级(哪些页面共享同一层 layout / loading / error)

1.2 结构建议(架构师视角)

  • 先用业务域拆 segment:/orders/*/users/*/products/*
  • 把“壳”放在 layout,把“内容”放在 page
  • 把鉴权/错误/加载边界尽量下沉到合适的业务域 segment(避免全局一刀切)

2. Layout:稳定 Shell 是体验与可维护性的核心

2.1 layout 的价值

  • 复用页面壳:导航栏、侧边栏、面包屑
  • 让导航时“壳不动、内容换”,减少切页闪屏
  • 给错误/加载边界提供更好的分层(局部失败不拖垮整站)

2.2 layout vs template(了解即可)

  • layout.tsx:跨导航复用(状态可保留)
  • template.tsx:每次导航都会重新创建(更像“每次都重置”)

大多数业务用 layout 足够;只有当你明确需要“每次进来都重置状态”才用 template。

3. Loading:把“等”变成可控的骨架分层

3.1 为什么 loading.tsx 很关键?

App Router 下,很多数据获取发生在 Server Components(RSC)里。只要某个 segment 树在等待数据,它就需要一个“等”的 UI。

loading.tsx 的价值在于:

  • 它把等待变成显式边界,而不是组件里到处 if (isLoading)
  • 它天然适合做页面壳 skeleton局部区块 skeleton

3.2 工程建议:优先保证稳定壳,再做局部骨架

  • 全局/高层 layout:尽量稳定(不要在 loading 时整页换壳)
  • 列表/详情等区域:在更低层 segment 做 skeleton

经验:Skeleton 的层级越合理,用户感知越“快”。

4. Error:错误边界要“能兜底 + 能上报 + 能恢复”

4.1 error.tsx 是什么?

  • 它是 segment 级错误边界
  • 它必须是 Client Component(通常要加 "use client"

4.2 设计要点

  • 给用户一个可理解的错误信息(不是白屏)
  • 提供重试(调用 reset()
  • 上报(Sentry/自研)要带:
    • route 信息(segment/path/searchParams)
    • release/version
    • traceId(如果你有全链路)

这里的上报策略会在“生产可用性”章节展开。

5. Not Found:把“无数据/无权限/真 404”区分开

5.1 not-found.tsx 的定位

  • 表达“资源不存在”或“无法访问该资源(以 404 语义呈现)”
  • 通常用于详情页:/posts/[id] 找不到

5.2 常见误区

  • 把所有错误都丢到 not-found:会导致监控缺失(500 被当成 404)
  • 把鉴权失败当成 not-found:要看业务(安全考虑可隐藏资源存在性,但要把错误上报到监控)

6. Prefetch:Next 的预取默认很强,但要理解边界

Next 的 <Link /> 默认可能会预取:

  • 目标路由的 RSC payload
  • 相关静态资源

这会带来:

  • 导航更快
  • 但也可能带来:带宽压力、数据提前拉取(与权限/个性化有关)

6.2 预取策略的工程化建议

  • 对“命中率高”的导航(主流程)保持默认预取
  • 对“命中率低/代价高”的路由:
    • 降低预取(按 Next 版本能力调整 prefetch
    • 或把数据获取延后到进入页面后(避免无效请求)

注意:预取不是越多越好,它是预算问题。

7. 与数据获取/缓存的关系(先给边界,细节放到第 12 章)

在 App Router 下,你会遇到两套“像缓存”的东西:

  • Next 的 fetch 缓存/重验证(框架级,影响线上是否更新)
  • 客户端的缓存(例如 TanStack Query,偏交互级)

架构建议(先立边界):

  • 首屏/页面级数据:更倾向于 Server Components + Next 缓存/重验证
  • 强交互数据(筛选、无限滚动、局部刷新):更倾向于 Client Components + TanStack Query

不要让同一数据域同时被两套缓存系统“各自为政”。

8. Checklist

  • segment tree 是否能表达清晰的信息架构(业务域分层)?
  • 是否存在稳定 shell(layout)来避免切页闪屏?
  • loading/error/not-found 是否分层(局部失败不拖垮整站)?
  • 是否理解 prefetch 的收益与成本,并有预算策略?
  • 数据获取边界是否明确(RSC vs Client Query),避免双重缓存真相?

关联阅读

cd ..