loading.tsx / error.tsx 与 Suspense 的对应关系use 在 App Router 数据读取中的角色传统 SPA 在客户端用 useEffect + fetch 取数,存在「先渲染空白再请求」的瀑布流。App Router 下,页面组件本身是 Server Component,可直接 async 取数:
// app/posts/page.tsx(Server Component)
export default async function Page() {
const posts = await db.posts.list();
return <PostList posts={posts} />;
}取数发生在服务端,客户端拿到的是带数据的 HTML/RSC 流,无需再发一次请求。
App Router 用约定文件表达 Suspense / ErrorBoundary 边界:
loading.tsx:等价于给当前路由套一层 <Suspense fallback>;error.tsx:等价于该路由的 ErrorBoundary(需 'use client');not-found.tsx:404 边界。这与 React 的 14-深入理解Suspense和ErrorBoundary 一脉相承,只是把边界「声明在文件系统」上。
当子组件是 Client Component 且需读取父级传入的 Promise,用 use(promise)(见 20-use Hook 实现原理)即可在客户端订阅该结果,避免 props 透传后再 useEffect。
react-router 的 loader / useLoaderData 与 App Router 思路相似,但前者仍在客户端路由层取数,App Router 利用 RSC 把取数搬到服务端。对比参见 00-React-Router。
App Router 的数据加载本质是「RSC + 路由约定」的组合:取数上移到服务端、边界用文件系统声明、use 衔接客户端读取。完整的工程化部署(缓存、revalidate)请参考本站 Next.js 分类。