本章定位
上一章,我们已经正式开始搭建 Next.js 多页面项目的基础骨架了。
你已经理解了这些很关键的事情:
- 路由本质上是地址和页面内容之间的对应关系。
app/目录、page.tsx和layout.tsx分别在负责什么。- 首页、列表页、详情页、关于页这类页面应该怎样分工。
- 导航、页头、页脚这类共用结构为什么适合放在布局层。
也就是说,到现在为止,你已经不只是知道“页面应该放在哪里”,而是已经开始具备:
组织一个多页面网站结构的基础能力。
但如果你继续往下做,很快就会遇到另一个非常现实的问题:
页面结构已经有了,可页面内容从哪里来?
例如:
- 文章列表页里的文章数据从哪里拿?
- 文章详情页怎么知道要展示哪一篇文章?
- 如果数据正在加载中,页面该显示什么?
- 如果接口失败了,页面是空白、报错,还是给用户提示?
- 如果列表没有任何数据,页面应该怎么表现?
这些问题说明我们已经从“页面结构”进一步走到了:
页面如何围绕数据组织展示。
所以这一章,我们会正式进入 Next.js 阶段非常核心的一条主线:
数据获取与页面渲染基础。
这一章不会一上来就把所有渲染模式讲得很复杂。
我们会先把最重要、也最适合初学者建立的几件事讲清楚:
- 页面数据通常从哪里来
- 静态内容、动态内容、用户交互内容有什么区别
- 列表页和详情页的数据可以怎样组织
- 为什么页面不能只写“成功显示”
- 加载态、错误态、空状态为什么同样属于页面设计的一部分
你可以把这一章理解成:
在真正做 Next.js 内容型网站之前,先把“页面围绕数据怎么呈现”这件事想清楚。
本章学习目标
学完这一章后,你应该能做到:
- 理解为什么学完路由和布局后,下一步自然就是数据获取。
- 知道页面数据不只有“接口请求”这一种来源。
- 理解静态内容、动态内容、用户交互内容三类内容的区别。
- 学会用一组本地文章数据建立列表页和详情页的数据直觉。
- 理解为什么即使是本地数据,也值得先通过函数进行组织。
- 理解列表页和详情页最基础的数据获取思路。
- 知道页面渲染不只有成功状态,还要考虑加载态、错误态和空状态。
- 理解这三类状态分别在解决什么问题。
- 知道初学阶段该如何逐步理解 Next.js 的数据获取场景。
- 为下一章学习组件复用、样式组织与公共模块做好准备。
一、为什么刚学完路由和布局,下一步就是数据获取
上一章我们解决的是:
页面放在哪里,页面怎么连起来,公共结构怎么组织。
这很重要,因为没有结构,项目就只是散的页面碎片。
但结构清楚以后,很快就会发现另一个问题:
页面不是只要有标题和容器就够了,它还需要真正的内容。
比如:
- 首页需要推荐文章
- 列表页需要文章数组
- 详情页需要某一篇文章正文
- 关于页可能需要作者介绍
这些内容,不一定都是手写在页面里的固定文字。
有些是固定内容,有些会变,有些还会跟用户操作有关。
所以学完路由和布局之后,最自然的下一步就是:
学会区分页面里的不同数据,并理解页面怎样根据这些数据渲染内容。
二、页面数据并不只有“请求接口”这一种来源
很多初学者一听“数据获取”,第一反应就是:
是不是就是
fetch接口?
这只对了一部分。
页面数据的来源,其实比这更丰富。
常见来源至少有这些:
- 写在代码里的固定内容
- 本地的数组、JSON、Markdown 文件
- 远程接口返回的数据
- 用户自己输入和操作产生的数据
这一步非常关键。
因为如果你把“数据”只理解成接口返回值,那么很多页面内容你就会看不清。
当前阶段更稳的理解是:
只要某段内容会影响页面显示,它就可以被看成页面的数据来源之一。
三、什么是静态内容
先看第一类:
静态内容
当前阶段你可以先这样理解:
静态内容,是那些在一段时间内相对固定、不需要频繁变化的页面内容。
例如:
- 网站标题
- 导航文案
- 关于页面里的固定介绍
- 联系方式
- 页脚版权信息
1. 为什么这类内容值得单独看
因为它们通常不需要每次都去请求接口。
很多时候,直接写在页面组件里,或者整理到本地常量文件中,就已经足够了。
2. 静态内容最适合什么页面
例如:
- 关于页
- 联系页
- 简单介绍页
- 固定文档页
这些页面不一定完全没有数据逻辑,但通常有一部分内容是相对稳定的。
3. 当前阶段最值得先建立什么意识
你可以先记住一句话:
不是所有页面内容都需要请求接口,固定内容本身也是一种很正常的数据来源。
四、什么是动态内容
再看第二类:
动态内容
当前阶段你可以先这样理解:
动态内容,是那些会随着时间、路径、条件或数据源变化而变化的内容。
例如:
- 文章列表
- 某篇文章详情
- 商品价格和库存
- 用户个人资料
- 评论数量
1. 动态内容为什么更值得注意
因为它通常意味着:
页面不能只靠写死的文字,而要根据某份数据源来决定展示什么。
2. 动态内容在内容网站里最常见的样子是什么
比如:
- 列表页根据文章数组渲染很多卡片
- 详情页根据
slug或id找到对应那篇文章
这两个场景非常典型,也是当前阶段最适合练习的两种页面。
3. 当前阶段你先怎么理解就够了
你可以先把动态内容理解成:
页面长什么样,不只由固定模板决定,还由一份会变化的数据决定。
五、什么是用户交互内容
第三类也很重要:
用户交互内容
当前阶段你可以先这样理解:
用户交互内容,是那些会随着用户输入、点击、切换、提交而变化的内容。
例如:
- 搜索框里的关键词
- 当前筛选标签
- 表单输入值
- 展开的折叠面板
- 当前分页页码
1. 为什么它和动态内容还不完全一样
因为它更强调:
页面不是只等外部数据过来,也会因为用户自己的操作实时变化。
2. 在 Next.js 项目里,这类内容常见吗
当然很常见。
例如:
- 搜索文章
- 切换分类
- 提交联系表单
- 展开文章目录
所以你要慢慢建立一个更完整的认识:
一个页面里,静态内容、动态内容、用户交互内容经常会同时存在。
六、先把这三类内容放在同一个页面里看一遍
为了让你更容易建立感觉,我们用一个“文章列表页”来举例。
假设页面上有这些内容:
- 页头里的网站标题“从零开始学前端博客”
- 一个文章列表
- 一个搜索框
那它们分别属于:
- 网站标题:静态内容
- 文章列表:动态内容
- 搜索框关键词:用户交互内容
这个区分非常重要。
因为它会帮助你在后面写页面时更清楚地判断:
- 哪些内容可以直接写死
- 哪些内容要从数据源拿
- 哪些内容要随着用户操作变化
七、先用一组本地文章数据建立数据直觉
当前阶段我们先不用急着接真实接口。
更稳的做法是先用一组本地文章数据,把:
- 列表页
- 详情页
- 空状态
这些最基础的渲染逻辑理解清楚。
先看一个简单的类型定义:
exportinterfacePostItem{slug:string;title:string;summary:string;category:string;publishDate:string;}再准备一组最基础的本地文章数据:
exportconstpostList:PostItem[]=[{slug:"react-introduction",title:"React 是什么,为什么流行",summary:"从组件化和数据驱动的角度理解 React。",category:"React",publishDate:"2026-07-01"},{slug:"nextjs-routing",title:"Next.js 页面路由与基础布局",summary:"从多页面结构理解 Next.js 路由和布局。",category:"Next.js",publishDate:"2026-07-05"}];1. 为什么这里先只放最基础字段
因为当前阶段我们的重点是:
先建立页面和数据之间的关系
而不是一上来就把文章模型做得非常复杂。
2. 为什么slug很关键
因为详情页通常需要依靠它来定位:
当前要展示的是哪一篇文章。
这和上一章讲的路径结构会自然接起来。
八、为什么即使是本地数据,也值得先用函数包一层
很多初学者第一次做页面时,会直接在页面里写:
const posts = postList;这样当然不是不行。
但当前阶段更稳的方式通常是:
先用一个函数把“拿数据”这件事包起来。
例如:
import{postList}from"./mock-posts";exportasyncfunctiongetPostList(){returnpostList;}1. 这样做有什么好处
最大的好处是:
后面如果从本地数组换成真实接口,页面结构不容易乱。
2. 当前阶段你最该建立什么意识
你可以先记住一句话:
页面应该更关心“拿到了什么数据”,而不是把数据来源细节全都揉进页面里。
这其实也是一种很基础的代码组织意识。
九、列表页怎样获取数据并渲染
现在来看最常见的第一种页面:
列表页
例如文章列表页,它的任务通常是:
- 拿到一组文章数据
- 把这组数据渲染成文章卡片或文章列表
当前阶段可以先这样写:
import Link from "next/link"; import { getPostList } from "@/lib/posts"; export default async function PostsPage() { const posts = await getPostList(); return ( <main> <h1>文章列表</h1> <ul> {posts.map(function (post) { return ( <li key={post.slug}> <Link href={`/posts/${post.slug}`}>{post.title}</Link> </li> ); })} </ul> </main> ); }1. 这一段代码最值得你先看懂什么
先看清这条主线就够了:
- 页面先拿到文章数组
- 再用
map把数据渲染成链接列表
2. 为什么这里看起来还是很像 React
因为 Next.js 页面本质上还是 React 组件。
只不过现在它放在:
- 路由结构里
- 页面文件里
- 更完整的应用上下文里
这也再次说明:
Next.js 阶段不是把 React 推翻重来,而是让 React 页面进入更完整的项目结构。
十、如果以后换成真实接口,思路会变吗
思路不会变。
真正变化的,往往只是“数据源”。
例如后面如果换成真实接口,数据函数可能会更像这样:
exportasyncfunctiongetPostList(){constresponse=awaitfetch("https://example.com/api/posts");if(!response.ok){thrownewError("文章列表加载失败");}returnresponse.json();}1. 这一节真正想让你抓住什么
不是先去记fetch细节,而是先建立一种更稳定的认识:
页面结构可以相对稳定,真正变化的只是数据从哪里来。
2. 为什么这对后面做真实项目特别重要
因为真实项目里,数据源经常会变化:
- 本地 mock 数据
- 测试接口
- 正式接口
如果一开始没有把页面和数据来源稍微分开,后面就会改得很痛苦。
十一、详情页的数据怎么理解
再来看第二种非常常见的页面:
详情页
详情页和列表页最大的不同是:
它不是展示很多条,而是展示“其中某一条”。
例如文章详情页通常会根据路径中的某个参数,找到对应文章。
当前阶段你可以先准备一个函数:
import{postList}from"./mock-posts";exportasyncfunctiongetPostBySlug(slug:string){returnpostList.find(function(post){returnpost.slug===slug;})??null;}1. 为什么这里适合用find
因为详情页的目标不是拿到一组结果,而是:
找到唯一匹配的那一条。
2. 为什么返回值要考虑null
因为路径有可能不存在,或者用户访问了一个无效地址。
这就意味着:
详情页并不一定永远能成功拿到那条内容。
这一步其实已经在为“错误和异常情况”做准备了。
十二、详情页怎样根据路径展示对应内容
当前阶段你先看核心主线就够了:
const post = await getPostBySlug(slug); if (!post) { return <p>没有找到这篇文章。</p>; } return ( <article> <h1>{post.title}</h1> <p>{post.publishDate}</p> <p>{post.summary}</p> </article> );1. 这里最关键的思路是什么
就是:
详情页不是凭空知道自己要显示什么,而是根据当前路径对应的标识去找那条数据。
2. 这和上一章的哪条主线连起来了
它正好和上一章讲的:
URL 地址和页面内容之间的对应关系
连起来了。
也就是说:
- 路由告诉你“现在是哪个页面”
- 数据查询告诉你“这个页面该显示哪条内容”
十三、为什么说数据获取不只是“拿到数据”
这一点非常关键。
很多初学者一开始做页面时,会把目标想得过于单一:
只要能把数据展示出来就行了。
但真实页面里,数据获取往往至少还伴随着这些问题:
- 数据还没回来时显示什么?
- 请求失败时显示什么?
- 数据为空时显示什么?
- 找不到目标内容时显示什么?
这说明一件事:
数据获取不只是“拿到值”,还包括页面如何表达不同状态。
这也是为什么这一章标题里不只是“数据获取”,还包括:
页面渲染基础
因为这两件事本来就是连在一起的。
十四、什么是加载态
先看第一种非常常见的状态:
加载态
当前阶段你可以先这样理解:
加载态,就是页面还在等待数据的时候,给用户看到的过渡状态。
例如:
- “文章加载中,请稍候……”
- 骨架屏
- 占位卡片
- loading 动画
1. 为什么加载态重要
因为如果页面什么都不显示,用户会不知道:
- 是页面坏了
- 还是正在加载
- 还是网络太慢
加载态的价值就在于:
告诉用户:页面没有卡死,数据还在路上。
2. 当前阶段先做到什么程度就够了
先做到最基础的清晰提示就够了。
例如:
if (isLoading) { return <p>文章加载中,请稍候……</p>; }不一定一开始就追求复杂骨架屏。
十五、什么是错误态
再看第二种状态:
错误态
当前阶段你可以先这样理解:
错误态,就是数据没有正常拿到时,页面给用户的反馈。
例如:
- 网络请求失败
- 接口返回异常
- 路径对应的数据不存在
- 数据格式不符合预期
1. 为什么错误态不能省
因为如果失败了却没有提示,用户只能看到:
- 白屏
- 半截页面
- 看不懂的报错
这些体验都很差。
更稳的做法是:
出错时,也给用户一个能看懂的状态。
2. 当前阶段最基础的错误态可以怎么写
例如:
if (errorMessage) { return <p>文章加载失败,请稍后重试。</p>; }这看起来很简单,但它的意义非常大。
因为它意味着你已经开始把“失败情况”看成页面设计的一部分。
十六、什么是空状态
第三种非常常见的状态是:
空状态
当前阶段你可以先这样理解:
空状态,就是页面逻辑正常,但当前没有可展示内容时给用户看到的状态。
例如:
- 文章列表还没有任何文章
- 搜索结果为空
- 某个分类下暂时没有内容
1. 为什么空状态和错误态不一样
这一点特别值得分清楚。
因为:
- 错误态是“本来应该拿到,但失败了”
- 空状态是“流程正常,只是当前没有内容”
2. 一个最基础的空状态可以怎么写
例如:
if (posts.length === 0) { return <p>当前还没有文章内容,稍后再来看看。</p>; }3. 为什么空状态也很重要
因为如果列表为空而你什么都不说,用户就会怀疑:
是我没加载出来,还是这里真的没有内容?
空状态就是在帮用户消除这种不确定感。
十七、为什么页面不能只写“成功状态”
这是本章最重要的认识之一。
很多初学者第一次写页面时,往往只会写这种情况:
数据成功拿到了,于是把列表渲染出来。
但真实页面至少通常会有这些可能:
- 正在加载
- 加载失败
- 加载成功但没有内容
- 加载成功且有内容
如果你只写最后一种,页面就会变得非常脆弱。
所以这一章你一定要尽早建立这个意识:
页面不是只有“成功显示”这一种状态,完整的页面至少要能表达等待、失败和为空。
十八、先看一个把三类状态串起来的基础示意
下面这个示意非常适合拿来建立直觉:
if (isLoading) { return <p>文章加载中,请稍候……</p>; } if (errorMessage) { return <p>文章加载失败,请稍后重试。</p>; } if (posts.length === 0) { return <p>当前还没有文章。</p>; } return <PostList posts={posts} />;1. 这段代码最值得你建立什么感觉
就是:
页面渲染不是一次性“成功就结束”,而是在根据当前状态选择合适的展示结果。
2. 这和你前面学过的哪种能力是连着的
它本质上仍然是:
条件渲染
只不过现在条件不再只是按钮开关、分类切换,而是更接近真实项目的数据状态。
十九、Next.js 里可以怎样逐步理解数据获取场景
这一节非常关键。
因为一提到 Next.js,很多人就会立刻碰到很多名词:
- 静态生成
- 服务端渲染
- 客户端获取
- 服务端组件
- 客户端组件
这些词当然都重要,但当前阶段更稳的方式不是死记术语,而是先按场景理解。
1. 第一类:内容提前就能确定
例如:
- 关于页面固定介绍
- 本地文章数据
- 一组课程说明文字
你可以先理解成:
这些内容在页面显示前就已经知道了。
2. 第二类:页面访问时需要拿最新数据
例如:
- 最新文章列表
- 商品库存
- 用户资料
你可以先理解成:
页面需要在展示前,先根据某个数据源准备内容。
3. 第三类:用户操作后再触发的数据变化
例如:
- 搜索
- 筛选
- 分页切换
- 表单提交
你可以先理解成:
这类内容更强调用户交互后页面如何继续更新。
4. 当前阶段最重要的不是术语,而是什么
是先建立这句话:
不是所有页面数据都来自同一个时机,也不是所有页面都用同一种渲染方式。
只要这层认识站稳了,后面你再去理解更具体的 Next.js 能力,就会轻松很多。
二十、列表页和详情页的数据思路为什么值得先练熟
因为内容型网站里,最常见的页面组合往往就是:
- 列表页
- 详情页
列表页负责:
展示一批内容的入口
详情页负责:
展示其中某一条具体内容
这两类页面几乎能覆盖很多实际场景:
- 博客
- 文档站
- 新闻站
- 课程站
- 商品站
所以当前阶段先把这两类页面的数据组织思路练熟,是非常划算的。
二十一、数据和页面结构应该怎样配合
这一点也特别重要。
上一章我们在讲:
- 哪些页面存在
- 页面怎样通过路径组织
- 公共布局怎样复用
这一章继续往前走,你会发现:
页面结构和数据结构最好是互相配合的。
例如:
/posts页面自然适合配文章数组/posts/[slug]页面自然适合配单篇文章数据/about页面更适合固定介绍内容
这说明一件事:
路由不是只决定页面地址,它也会影响页面需要什么数据。
这是非常关键的项目思维。
二十二、一个适合初学者的基础组织示例
当前阶段你可以先用下面这种结构来建立项目感觉:
src/ ├── app/ │ ├── layout.tsx │ ├── page.tsx │ ├── about/ │ │ └── page.tsx │ └── posts/ │ ├── page.tsx │ └── [slug]/ │ └── page.tsx ├── components/ │ ├── post-card.tsx │ └── empty-state.tsx └── lib/ ├── mock-posts.ts └── posts.ts1. 为什么这里把数据放进lib/
因为当前阶段更适合让页面去调用:
获取数据的函数
而不是让页面直接和原始数据细节缠在一起。
2. 为什么这里开始出现empty-state.tsx
因为从这一章开始,你已经不只是在写“成功显示的页面”,而是在开始组织:
不同页面状态下的展示组件。
这是一种很重要的页面完成度意识。
二十三、页面数据组织时,初学者最容易踩的几个坑
这一节建议你认真看。
因为这一章开始,很多问题表面上像是“请求没写对”,本质上其实是页面状态意识还没建立起来。
1. 坑一:把“数据获取”只理解成接口请求
这样会导致你看不清:
- 固定内容
- 本地数据
- 用户输入状态
这些同样会影响页面展示的内容。
2. 坑二:页面里只写成功状态
这样一旦网络慢、数据空、接口失败,页面就会变得很脆弱。
3. 坑三:页面直接写死所有数据来源细节
这样后面从本地数组换成真实接口时,页面会改得很乱。
4. 坑四:把错误态和空状态混为一谈
这两者完全不是一回事。
- 错误态是失败
- 空状态是成功但没内容
5. 坑五:一上来就被复杂渲染模式吓住
当前阶段最重要的不是立刻区分所有术语,而是先把这些最基础的事情想清楚:
- 数据从哪里来
- 页面可能有哪些状态
- 路由和数据怎么对应
6. 坑六:只顾着拿数据,不去想用户能看到什么
要记住:
页面不是给代码看的,而是给用户看的。
所以数据获取的最终目标,不是“控制台里有值”,而是:
用户在不同状态下都能得到清楚反馈。
二十四、本章实践练习
这一章的练习重点,是把“数据来源”和“页面状态”真正联系起来。
1. 练习 1:做一个本地文章列表页
请你准备一组本地文章数据,并在posts/page.tsx中把它渲染成列表。
要求:
- 至少有标题和摘要
- 每项都可以点击进入详情页
这个练习会帮助你建立:
列表页 = 一组数据 + 一组入口
的基础感觉。
2. 练习 2:做一个最基础的文章详情页
请你根据slug去找到对应文章,并在详情页展示:
- 标题
- 日期
- 摘要或正文片段
这个练习会帮助你把:
路由参数和单条数据之间的关系
真正串起来。
3. 练习 3:给列表页补上空状态
请你尝试把文章数组临时改成空数组,并设计一个空状态提示。
这个练习的重点是:
让你真正意识到页面不是只有“有数据时的样子”。
4. 练习 4:给页面写一段错误提示文案
假设接口失败了,请你为文章列表页和详情页各写一段用户能看懂的错误提示。
这个练习会帮助你从一开始就建立:
错误反馈也属于页面体验的一部分。
二十五、学习重点提示
这一章请你重点记住下面这些话:
- 页面数据不只有接口请求,还可能来自固定内容、本地数据和用户交互状态。
- 静态内容、动态内容、用户交互内容经常会同时出现在一个页面里。
- 列表页通常在展示一组数据,详情页通常在展示其中某一条数据。
- 即使现在数据是本地数组,也值得先通过函数去组织获取方式。
- 数据获取不仅是“拿到数据”,还包括页面怎样表达加载中、失败和为空。
- 错误态和空状态不是一回事。
- 页面不能只写成功状态,完整页面至少应该考虑等待、失败、为空和成功。
- 学习 Next.js 数据获取时,先按场景理解,比一开始死记所有术语更稳。
如果你只记一句话,请记住:
这一章真正要建立的,不只是“会把数据渲染出来”,而是“会让页面围绕不同数据来源和不同状态清楚地呈现内容”。
二十六、本章小结
这一章,我们正式把 Next.js 从“页面结构和路由”推进到了“页面如何围绕数据进行展示”的阶段。
你已经理解了:
- 页面数据不只有接口这一种来源
- 静态内容、动态内容、用户交互内容分别是什么
- 列表页和详情页最基础的数据组织思路
- 为什么即使是本地数据,也值得先通过函数组织获取方式
- 为什么页面不能只写成功状态
- 加载态、错误态、空状态分别在解决什么问题
- 数据结构和页面结构应该怎样互相配合
更重要的是,你开始真正建立一种很关键的页面完成度认知:
一个页面是否“像项目”,往往不只看它有没有数据,而要看它能不能在不同状态下都给用户清楚反馈。
这一步非常关键。
因为从这里开始,你已经不只是在搭页面骨架,而是在真正进入:
Next.js 页面数据组织与渲染思维的基础阶段。
二十七、课后思考题
请你认真思考下面这些问题:
- 为什么说页面数据不只有接口请求这一种来源?
- 静态内容、动态内容、用户交互内容的区别分别是什么?
- 为什么列表页和详情页是内容型网站最值得先练熟的两类页面?
- 为什么即使是本地数据,也值得通过函数去组织获取方式?
- 为什么错误态和空状态不能混为一谈?
- 为什么说页面不能只写成功状态?
- 如果你要做一个博客文章页,哪些内容更像静态内容,哪些内容更像动态内容,哪些内容又属于用户交互内容?
建议你把这些问题用自己的话写下来。
只要你能把这些问题讲清楚,说明你已经真正进入 Next.js 页面数据组织与渲染的主线了。
二十八、下一篇预告
下一章我们会继续进入:
从零开始学前端 | 第四十章:组件复用、样式组织与公共模块
你会开始真正接触这些内容:
- 公共组件应该怎样提取
- 页面组件和通用组件怎么分工
- 样式文件、工具函数、常量配置放在哪里更清楚
- 为什么项目越往后做,越要重视结构分层
也就是说,下一章开始,我们会从“数据获取与页面渲染基础”,继续走到:
Next.js 项目结构整理与组件复用的下一步。