Next 权衡录

把选型放到同一把尺子上

首页Server/Client 边界渲染策略路由机制数据与缓存Metadata 与 SEO工程化安全AI-Native 与 Agent

路由机制

文件系统路由、约定文件与导航缓存

动态路由与动态 metadata约定文件对照错误与未找到平行路由与拦截导航与 Router Cache路由组与多根布局国际化路由

Next 权衡录

把选型放到同一把尺子上

文档写能做什么。这里写该选哪条。

专题分类

  • Server/Client 边界
  • 渲染策略
  • 路由机制
  • 数据与缓存
  • Metadata 与 SEO
  • 工程化
  • 安全
  • AI-Native 与 Agent

资源

  • Next.js 文档
  • React 文档
  • Tailwind CSS
  • Vercel 部署
  • Next.js GitHub

© 2026 Next 权衡录 · Next.js 16 + React 19 + Tailwind CSS 4

8 个领域 · 37 个专题

路由机制/约定文件对照

约定文件对照

page / layout / loading / error / not-found / template / default 各管什么:一张对照表 + 嵌套层级示意;本页自带真实 loading.tsx 演示 Suspense 边界

文件 → 职责 → 渲染时机

文件名即配置:放在哪一级目录,就管哪一级路由段

约定文件职责渲染时机必须 Client?
page.tsx该路由段的页面主体,export default 即页面每次访问该路由时否(默认 Server)
layout.tsx包裹本段及所有子段的共享外壳进入该段时挂载,段内导航保持不卸载否
loading.tsx给本段 page 自动包一层 Suspense 的 fallback页面渲染(取数)未完成期间否
error.tsx给本段包一层 Error Boundary,接住渲染错误本段渲染抛错时替换 page是
not-found.tsxnotFound() 抛出时的兜底 UI本段调用 notFound() 或访问未匹配路由时否
template.tsx与 layout 同位的外壳,但每次导航重挂载每次进入本段都重新挂载否
default.tsx并行路由(@slot)在当前 URL 下无匹配时的兜底并行路由槽位失配 / 硬刷新恢复时否

嵌套 layout 的层级(本站真实结构节选)

layout 是套娃:每多一层目录就多一层可选外壳;导航时只有叶子 page 被替换

app/
├── layout.tsx          ← 根布局:全局唯一,<html>/<body> 只能在这写
│   ├── page.tsx        ← 首页(路由 /)
│   └── router/
│       ├── layout.tsx  ← 嵌套布局:包 /router 下所有页(可选,没有就透传)
│       ├── dynamic-routes/
│       │   ├── page.tsx        ← 列表页
│       │   └── [id]/
│       │       └── page.tsx    ← 详情页:Server + generateMetadata
│       ├── errors/
│       │   ├── error.tsx       ← 渲染失败(必须 Client)
│       │   ├── not-found.tsx   ← notFound(),不进 error.tsx
│       │   └── page.tsx
│       └── conventions/
│           ├── loading.tsx     ← 本页的骨架屏(本页的活例子)
│           └── page.tsx        ← 你正在看的页面

渲染时的包裹关系(由内向外套娃):
  RootLayout → RouterLayout → [Id]Layout → Page
每个 layout 保持挂载不卸载,导航时只有 page 段会换

详情页没有再套一层只为了标题的 layout:page 保持 Server Component, generateMetadata 与页面写在一起。见动态路由专题。default.tsx 的活例子在平行路由。

活演示:本页的 loading.tsx 就是一个真约定文件

loading.tsx 的本质:Next 自动把本段 page 包进 <Suspense fallback={Loading}>

  • 源码见app/router/conventions/loading.tsx—— 一个与 TopicSection 外形对齐的骨架屏,首次渲染本段时由它兜底
  • 本页是纯静态内容,渲染几乎瞬间完成,骨架大概率一闪而过; 想看明显的 loading 效果,对照/rendering/streaming—— 那里手写 Suspense 分段流式,loading.tsx 是它的「段级自动版」
  • 推论:loading.tsx 不影响数据加载本身,它只是把「等待」变成可设计的产品状态; 边界粒度决定哪个区块先出现,整段共用一个 loading 意味着整段一起等

什么时候别用:template vs layout、error 为何必须是 Client

决策要点:重挂载是特性不是免费;Error Boundary 在服务端没有意义

// app/template.tsx —— 与 layout 同位置、同形状,但语义不同
export default function Template({ children }: { children: React.ReactNode }) {
    // 每次在本段内导航,template 都会整体重挂载:
    //   - 内部 state 清零
    //   - effect 重新执行
    //   - 每次进入都重新触发一次进入动画
    return <div>{children}</div>;
}

// layout.tsx 则相反:导航时保持挂载,state 保留、effect 不重跑。
// 因此默认用 layout;只有「每次进入都要从头再来」的场景才用 template
  • 别拿 template 当默认:每次导航重挂载意味着 state 清零、 effect 重跑、DOM 重建;只有「每次进入都要重置」的场景(进入动画、 每页独立埋点初始化)才值得付这个代价,其余一律 layout
  • error.tsx 必须是 Client Component:它本质是一个 React Error Boundary,需要在渲染失败后用 state 接住错误并提供 reset() 重试 —— 这套机制只存在于客户端运行时,服务端渲染流里没有 「重试已失败的子树」这回事。因此写 error.tsx 时第一行必然是"use client"
  • 别指望 error.tsx 接住一切:它只接本段 page 及其子树的渲染错误, 接不住同级 layout 自身的错误,也接不住事件回调里的异步异常(那要 try/catch)。 按下去看效果:错误与未找到
相关专题路由组与多根布局

延伸阅读

  • Next.js 文档:File Conventions 总表
  • Next.js 文档:Layouts and Pages