从零开始学前端 | 第三十九章:数据获取与页面渲染基础

从零开始学前端 | 第三十九章:数据获取与页面渲染基础 本章定位上一章我们已经正式开始搭建 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(){constresponseawaitfetch(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.slugslug;})??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 项目结构整理与组件复用的下一步。