66-Data Fetching

image-20250914102512780

image-20250914102600085

创建新项目:npx create-next-app data-fetching-demo

67-Fetching Data in Client Components

使用https://jsonplaceholder.typicode.com/提供的api来获取数据。在client components里面获取数据,其实之前就学过,和react组件里面获取数据一样,只不过在nextjs中,需要使用use client将组件变为client components。

下面这段代码重点关注里面获取数据的部分。

可以看到,先出现Loading,数据返回后渲染了数据。

提示:

在client components里面获取数据,只在一些特定的情况下推荐使用,否则都应该在server components里面获取数据,哪些情况呢?

like when you need real-time updates, or when your data depends on client-side interactions that you can't predict on the server side,

68-Fetching Data in Server Components

image-20250914105709626

在server components里面请求数据,就像写普通的js代码那样。

可以看到数据正常显示了:

针对server components的数据请求,react有优化:

image-20250914110744940

react will deduplicate fetch requests with the same URL and options。react 会将具有相同URL和传参的fetch请求去重,使用第一次请求获取的数据。

image-20250914110118082

这意味着可以在component tree的任何位置请求数据,而不用担心重复的网络请求。因为react针对相同的URL和传参,只会执行一次fetch操作,并在同一渲染传递中的后续调用中重复使用结果。

这样做的好处是什么呢?可以让我在需要data-fetching的时候就使用它,而不用集中处理数据请求并通过props来传递数据。

69-Loading and Error States

在client components中,我们使用useState来定义loading和error的,那在server component里面,不能使用useState,怎么展示loading和error的状态呢?

只需要定义loading.tsx和error.tsx这两个文件即可,之前已经学过了,这里丰富一下内容。

image-20250914111456593

为了查看loading的效果,在users-server/page.tsx里面加一个延迟,如果不会就看前面学习的内容。看一下效果:

为了查看error的效果,将请求地址修改为错误的地址,查看:

70-Sequential Data Fetching

image-20250914112454130

并行请求是我们常做的,而顺序请求在一些情况下是必须的,比如说要先获取某个组织,然后获取某个组织下的人员列表。

image-20250914115603534

下面专门讲解顺序请求。案例需求:获取所有博客,然后获取每一个博客里面的作者信息。

image-20250914114052522

先创建博客列表页面:

查看效果:

下一步就是将每一个post里面的author name获取到,使用/users/1这个接口。怎么获取呢?这种写法之前还真的没有见识过,是编写一个author.tsx组件,父组件传递userId过去,再请求数据。

为什么可行呢?因为父组件里面使用了async...await,userId是肯定有的,那么在author.tsx里面请求数据就没有问题。

在父组件中使用author.tsx:

image-20250914130959160

可以看到名字都显示出来了。

image-20250914130927286

这里模仿一下获取name网速慢的情况,在author.tsx里面添加延迟操作await new Promise((*resolve*) => setTimeout(resolve, 3000));

可以看到把整个页面的渲染都阻塞了,这时候就要想到使用Suspense来解决卡顿问题:

image-20250914134808385

效果:

可以看到,虽然接口被阻塞了,但是其余的页面及时渲染了,这样的效果很好。

 

这个免费的API接口,浏览器可以使用,但是server component里面不能使用,找不到原因。放到API FOX上测试,显示:"Connection was forcibly closed by a peer"。链接被对方强制关闭了。

应该是有请求次数的限制,学习的时候老是出不了效果怎么办?还是自己做一个后端服务吧。我做了一个jsonplaceholder的服务端,一切都好起来了。

71-Parallel Data Fetching

image-20250914120334634

这个案例里面,展示同一个用户的posts和albums,这样请求/posts?userId=1/albums?userId=1,那么当进入到用户详情页面时,这两个接口可以同时发起请求。这就是parallel data fetching的意义。

查看效果:

现在为每一种数据请求添加一个延时,并且添加loading效果。

 

image-20250914184054538

image-20250914184109355

这是老师说的,但是后端能不能接受还需要询问。

 

这里有另外一点需要注意:

因为这里是server components,所以浏览器的network面板里面没有这两种数据请求,我是说怎么找都找不到呢。如果要调试的话,只能在terminal里面输出来看。

72-Fetching from a Database

image-20250914184350652

这节课开始学习如何从数据库获取数据。

image-20250914184645777

参考文档:https://www.prisma.io/docs/guides/nextjs

下面的步骤是在nextjs中添加prisma和sqlite,这些步骤刚开始不熟悉是很正常的,多看、多用就会了。

1、npm i -D prisma安装prisma。

2、使用sqlite初始化prisma,npx prisma init --datasource-provider sqlite。这个命令报错。

image-20250914190822409

解决办法:执行这个代码:npx prisma init。会在项目的根目录生成一个prisma/schema.prisma的文件,修改里面的内容如下:

重要的就是修改datasource db里面的内容,provider改为sqlite,url就是db文件的地址,注意generate client里面的output属性,需要去掉。

问了grok。

默认行为:如果不指定 output,Prisma 会自动将生成的客户端代码放到 node_modules/.prisma/client 目录中。这是最常见的设置,尤其在标准 Next.js 项目中。它不会污染你的源代码目录(如 src),并且在运行 npx prisma generate 时会自动处理。

image-20250915085244825

这一步没有安装sqlite,难道不需要安装吗?

image-20250915085616999

3、npx prisma migrate dev --name init,生成prisma迁移和客户端。

prisma使用迁移来同步模型到数据库。如果以后新增、修改、删除了schema.prisma里面的modal,需要使用这个命令来更新。

这一步执行之后,会在prisma文件夹里面生成这些文件,生成了这些文件之后,下一步就可以使用了。

注意开发过程中要将生成的.dev.db文件放入到.gitignore中去,因为数据库文件一般不放入git。

image-20250915090003527

4、创建src/prisma-db.ts文件,在里面编写数据库操作的代码。不要管里面的await new Promise的延时代码,这些只是为了模拟的:

5、在server components里面使用prisma。直接使用定义好的方法。

效果:

这里有一个卡顿,是因为加了await new Promise。实际上是很快的。

这里的从数据库获取数据和我想象的不太一样,或许这就是以前的前后端不分离的做法,但是我之前并没有见过。

 

我想象的还是编写后端接口,前端使用axios或者fetch来调用接口。没有想到这里是直接使用方法名了。

 

实际上还是可以先定义route.ts,在里面使用prisma,然后在组件里面调用接口。不过直接调用方法名是更简单的做法。具体还是看项目需求。我肯定是直接使用方法名了。

 

2025-10-22 今天想专门学习prisma,看到web dev simplified这个老师讲解的配置prisma的过程,和Codevolution的一致,而且配置的没有问题。我决定重新使用Codevolution老师在课上的配置方式来试一下,结果真的可以。

 

 

73-Data Mutations

data mutations是什么意思?数据突变?可以理解为数据CUD的操作,相当于nextjs提出的一种专有名词,显得有逼格。

image-20250915093043370

image-20250915092404648

 

image-20250915092547371

首先让我们看一下在react里面,怎么实现数据的CUD。

新建app/react-form/page.tsxapp/react-form/api/route.ts

可以看到,page.tsx里面定义的变量有很多,不过整个流程看起来确实还是蛮清晰的。

可以看到有一个小卡顿,这是因为接口里面写了await new Promise的原因,后面会解决这个问题。

74-Forms with Server Actions

image-20250915094532270

image-20250915094556786

server actions的约定如下:

1、在async function里面的顶部(这里老师的英文说的不准确),使用"use server";来标记这个函数是server action。

2、在一个单独的文件里面,顶部使用"use server";,来标记这个文件导出的所有函数都是server actions。

有了server action,可以做什么呢?下面以一个案例来说明,还是新增product的需求。

新建products-db-create/page.tsx,从react-form/page.tsx里面复制return的内容,把里面的onSubmit、onChange事件,loading变量都去掉,变成这样:

在组件内创建server action函数,在函数内部的顶部使用"use server"来标记。

下一步,将函数赋值给form的action属性,将函数和form连接起来。

当有人提交表单时,createProduct函数会自动接收form data作为参数,将参数接收之后,就可以直接调用prisma的方法。

注意:为什么formData里面可以获取到具体的form-item的值?因为无论是input还是textarea,都指定了name属性,就是根据name属性获取的值。

查看效果:

可以看到提交成功了。

以下是server actions的好处:

image-20250915102711530

下面就第4点好处来说明,打开/products-db-create页面,在Sources面板里面,使用ctrl+shift+p,输入disable javascript禁用js。

image-20250915102850066

然后查看是否能够正常提交表单。

可以看到能够正常提交表单。使用ctrl+shift+p,输入enable javascript,启用js。

75-Pending State with useFormStatus

form提交的时候,我们想添加按钮禁用功能,避免用户的重复提交。这时候需要使用useFormStatus方法,可以获取到最近一次的form提交的相关参数。

image-20250915103428661

这节课主要专注于里面的pending参数。

image-20250915103449646

因为useFormStatus是react hook,所以只能在client components里面使用。但是为了使用这个hook,将整个form组件变为client component,显然是不行的。

可以将submit按钮抽出来,成为单独的client component。为什么可行呢?因为useFormStatus返回的是最近一次form的提交,与具体的form其实是解耦的,所以submit组件里面能够获取到准确的信息。而且button点击后会触发form的action,其实这也是与form解耦了。

上面这段解释非常重要,因为我无论是使用jQuery还是vue,我都是直接在button上面绑定事件的,button和form其实是不能分开的,但是react里面就不一样了。

products-db-create/page.tsx里面的按钮替换为这个组件<SubmitButton />,查看效果:

可以看到,点击提交之后,按钮就变为不可点击了。

76-Form Validation with useActionState

image-20250915105427306

这节课告诉我们怎么使用useActionState,但是这节课最终没有完全解决问题,要等到下一节课才能完全解决。

先看怎么使用useActionState

1、定义错误类型、form表单的状态类型(只需定义里面的errors属性)

2、在组件中定义初始状态,并使用useActionState关联form action函数和初始状态

3、在form action函数中,验证表单数据,并设置错误消息。如果errors对象有属性值,那么就返回errors对象

4、useActionState返回3个参数,state是表单当前状态,里面有errors;formAction,是一个form action函数,需要使用这个函数替换掉createProduct;isPending,表单提交的pending状态。

因为state里面有errors对象,所以在jsx代码中,添加警告的代码。

这节课中,先不使用之前定义的Submit按钮组件,使用普通的按钮,将isPending状态加上去。第78节课会详细说明二者的区别。

此时我们会遇到一个错误:

image-20250915113651577

useActionState这个react hook只能用在client components中,那么我们将这个组件变为client components之后,又有新错误:

image-20250915113800102

It is not allowed to define inline "use server" annotated Server Actions in Client Components. To use Server Actions in a Client Component, you can either export them from a separate file with "use server" at the top, or pass them down through props from a Server Component.

不能在client components里面使用"use server",提示信息还告诉我们怎么解决:要么将代码单独抽离出来;要么当作props传递过来。

下节课中我们使用第一种方法来解决问题。

77-Separating Server Actions

我们抽离server action函数到一个单独的文件中去,这样就可以保证使用use server没有问题。哪部分用到了use server,就抽离那部分。

创建src/actions/products.ts,这文件的顶部使用"use server";directive,将整个文件的内容都变为服务端使用。

products-db-create/page.tsx里面引入ts类型和函数,使用即可。

image-20250915114759457

useActionState的第一个参数有个报错,这是因为第一个参数的函数createProduct里面,第一个参数是prevState,加上之后就不报错了,这里的prevState暂时不使用。

image-20250915120824726

测试一下:

可以看到必填项的校验触发了。