错误与未找到

error.tsx 接渲染失败,not-found.tsx 接 notFound(),unauthorized/forbidden 接 401/403 认证中断;global-error.tsx 是根布局崩溃时换掉整个文档的最后一道

按下去,看本段 error.tsx

抛错发生在渲染阶段。按钮的 onClick 本身不会被 Error Boundary 接住,所以用一次 state 更新把异常留到下一次渲染

触发后,这个专题页会被app/router/errors/error.tsx换掉。顶栏还在,因为错误边界包的是这一段,不是根布局。global-error.tsx才会换掉根布局 —— 本站现在有真实的app/global-error.tsx了(见下方专节),但不拿线上壳层做触发实验。

打开一条不存在的地址

notFound() 不是异常。error.tsx 接不住它

打开 /router/errors/missing

那个路由的 page 直接调用 notFound(),由同段的 not-found.tsx 渲染。 预期中的空(用户被删了、id 不存在)走这条;渲染崩溃走 error.tsx。

五份文件各管什么

先问这是失败,还是本来就没有;认证中断与 404 一样是「中断」,不是异常

文件什么时候出现必须是 Client?
error.tsx本段渲染抛错是。它要提供 reset()
not-found.tsx调用了 notFound(),或没有匹配的页面否
unauthorized.tsx调用了 unauthorized()(HTTP 401,未认证)否
forbidden.tsx调用了 forbidden()(HTTP 403,已认证但没权限)否
global-error.tsx根布局自己坏了是,并且要自己写 html 和 body

按下去,看 401 与 403

unauthorized() / forbidden() 与 notFound() 同族:抛出即中断渲染,交给最近的同名约定文件,不进 error.tsx

  • 语义分工:401 回答「你是谁」(未登录、会话过期, 登录入口该出现在 unauthorized.tsx 里);403 回答「我知道你是谁, 但你没权限」(角色不足,引导换账号/申请权限)
  • 两个演示页的闸门都先await connection()再抛 —— 权限是请求时决策,不能出现在预渲染阶段 (cacheComponents 约束),所以闸门包在 Suspense 里
  • 真实项目里闸门的位置是「读 session → 分支」;本站没有登录体系, 固定演示两个失败分支。与notFound()一样,它们抛出的不是 Error,error.tsx 接不住也不该接

global-error.tsx:根布局崩溃时的最后一道

本站真的有这份文件了 —— app/global-error.tsx,这里直接引用它的源码事实

  • 边界:error.tsx 只能接「本段 page 及子树」的错误, 接不住根 layout 自己的崩溃;根布局坏了时,app/global-error.tsx替换的是整个文档,顶栏侧边栏全军覆没
  • 必须自带 <html>/<body>:根布局被换掉了, 文档骨架没人提供 —— 这是全站唯一一个自己渲染 html/body 的非根布局文件
  • 必须是 Client Component:与 error.tsx 同理, reset() 依赖客户端 Error Boundary;本站的实现第一行就是"use client"
  • 全内联样式,零外部依赖:根布局崩溃意味着 globals.css、 字体变量都可能不可用,本站的 global-error 只用内联样式 + 品牌底色 + signal 磷光,连 Tailwind 类都不写
  • 为什么不做活演示:触发它得让根布局真的抛错, 那会把全站(包括导航回专题页的能力)一起打挂;读app/global-error.tsx源码就是本站选择的演示方式