shadcn和tailwindcss一起使用,shadcn组件的样式丢失

其实这个问题我2025-09月份就遇到了,但直到现在2026-01才想到要解决。

原因1:tailwind.config.js 的 content 路径没包含 shadcn 组件目录

安装了二者之后,tailwind.config.js里面不会自动包含shadcn的组件目录,所以需要自己添加:

原因2:确认全局样式文件被正确引入(通常在根布局)

早就该解决了。

pnpm 常用命令

命令常用别名作用说明相当于 npm 的写法
pnpm installpnpm i安装 package.json 里全部依赖(最常用命令)npm install
pnpm add 添加依赖到 dependencies 并安装npm install
pnpm add -D pnpm add --save-dev添加到 devDependencies(开发依赖,如 eslint、vite 等)npm install -D
pnpm add -g 全局安装(如 pnpm 本身、typescript、create-vite 等)npm install -g
pnpm remove pnpm rm、pnpm un删除依赖(会同时从 package.json 移除)npm uninstall
pnpm updatepnpm up、pnpm upgrade更新所有依赖到 package.json 范围内最新版npm update
pnpm update pnpm up 只更新某个包npm update
pnpm listpnpm ls以树状结构列出所有依赖(可加 --depth 0 只看直接依赖)npm ls
pnpm dlx 临时执行某个包的命令(不用全局安装,如 pnpm dlx cowsay hello)npx
pnpm init初始化项目,生成 package.jsonnpm init
pnpm --versionpnpm -v查看 pnpm 版本npm -v

配置prisma和postgresql

参考:https://www.prisma.io/docs/getting-started/prisma-orm/quickstart/postgresql

1、安装依赖

2、初始化prisma orm

3、编辑.env文件,添加数据库地址

我使用的是neon,它提供5G的存储空间,够用了。使用的是google账号登录的。

image-20251201123841924

每一个新项目最好创建一个新数据库,因为prisma操作会同步数据库,如果使用现有的数据库,会造成冲突。那么就点击create database,会创建一个新数据库。

image-20251201123937269

将这个地址粘贴到.env文件里面即可。

image-20251201124006970

4、在prisma/schema.prisma文件里面,定义data model

5、创建和应用迁移

只要model变更了,就执行这两个命令:

6、初始化prisma client

创建lib/prisma.ts

prisma v6版本:

prisma v7版本:

 

7、接下来就可以在lib文件夹里面写相关的数据库操作代码了,先引入定义好的prismaClient,然后正常写代码。

8、添加模拟数据

注意:

postgresql里面的字段名要采用下划线的形式,不要采用小驼峰的形式,下划线是SQL规范。在prisma里面定义的时候也要注意。

但是这样做之后,在组件代码里面使用字段名有不方便了,JS里面使用下划线比较少见,更多的是小驼峰的形式,这该怎么办呢?

 

prisma有两个方法来帮助我们,@@map()方法和@map()方法,在定义表的时候,在表的最后面加上表的复数形式@@map("comments"),这样prisma在postgresql创建表的时候,就会自动将数据表的名称变为comments,而不是大驼峰的形式。

image-20251201164127109

https://www.prisma.io/docs/orm/prisma-schema/data-model/models#defining-models

@@map ——模型映射(Model/Table Mapping)

作用:指定 Prisma model 对应数据库中的表名。

如果 Prisma 模型名是大驼峰(默认是),但你希望数据库用复数表名或蛇形命名,则使用 @@map.

简单来讲,@@map()里面的参数就是定义model名称的复数形式,你指定是什么就是什么,如果不知道英文单词的复数,就搜索一下即可。

 

@map ——字段映射(Field Mapping)

作用:指定 Prisma 字段对应数据库中的实际列名。

默认情况下 Prisma 会直接使用字段名作为数据库列名。如果你使用了小驼峰、想让数据库是下划线,就要用 @map。比如说createdAt,用的时候是这样用,但是在定义的时候,postgresql里面需要定义为下划线的形式,就需要这样指定:createdAt DateTime @default(now()) @map("created_at")

下面是一个model样板。

使用https://mockaroo.com/来创建模拟数据。

image-20251201131408706

需要注意的是datetime,要选择SQL datetime这种类型,否则格式不对。

image-20251201132008227

这里就是SQL的插入语句,复制

image-20251201131453773

在编辑器中修改表名,粘贴到neno SQL Editor里面执行即可。这种方法我试过了,太复杂了,所以还是使用本地编辑器导入csv文件来做吧。

neon的链接地址:

➤ Host(服务器地址)

➤ Port

Neon 默认 PostgreSQL 端口是:

➤ Database

➤ Username

➤ Password

粘贴进去即可连接。然后导入数据。

image-20251201133801621

image-20251201133853533

注意:

Prisma 遵循 camelCase (驼峰命名法) 定义模型字段,但大多数传统的 PostgreSQL 数据库默认使用 snake_case (下划线命名法) 作为物理列名。所以我在添加数据的时候,如果是这样的语句,会报错createat column does not exist

所以需要改为这样craete_atupdate_at,也就是说凡是有小驼峰命名的地方,都要改为下划线命名:

dbeaver连接neon保持不掉线

原因

✅ 原因 1:Neon Serverless 会自动断开空闲连接

Neon 是 serverless Postgres,为了节省资源,它会自动关闭长时间空闲的连接。

默认情况(Neon 免费 tier):

行为Neon 的做法
连接 5 分钟无活动会自动关闭连接
空闲到达阈值会 suspend compute(自动休眠)
再次连接需要唤醒 → 所以你看到“重新连接”提示

这就是为什么 DBeaver 停在那里不动一会儿之后,连接突然失效。

✅ 原因 2:你使用的是 Neon 的 \pooler\ 端点

你现在的连接串是这样的:

Neon 的 Pooler 也有自己的超时机制:

机制描述
pooler_idle_timeout连接一段时间不活跃会被关闭
pooler_max_client_conn超过总连接数会丢弃/挤掉连接

所以 DBeaver 会被踢掉是完全正常的。

✅ 原因 3:DBeaver 默认不会自动保持心跳

DBeaver 对 PostgreSQL 不会默认发心跳包(KeepAlive)。

因此:

= 连接被断开,必须重新连接。

解决办法

image-20251201183925793

nextjs配置访问外部图片白名单

nextjs的Image组件,不能直接访问外部图片链接,需要在next.config.ts里面进行配置:

image-20251201141605609

app router里面文件夹和组件的命名风格

文件夹命名风格

一句话,采用kebab-case(小写 + 连字符)来命名。

Next.js App Router(即 app/ 目录下的路由系统)对文件夹命名风格有比较明确的社区共识和推荐做法,虽然官方文档没有强制规定大小写,但强烈建议统一风格来保持 URL 的干净、可读性和一致性。

1、推荐的文件夹命名风格(2025-2026 年主流做法)

你的命名 aboutMe/page.tsx 可以吗?

技术上可以运行,Next.js 不会报错,访问 /aboutMe 也正常。

但不推荐,原因如下:

2、推荐改成这样(最佳实践)

或者更简洁:

3、完整推荐命名对照表(App Router)

类型推荐风格示例文件夹/文件对应 URL备注
普通路由文件夹kebab-caseuser-profile, blog-posts/user-profile最常见,URL 最友好
动态路由文件夹kebab-case + [][userId], [slug]/{动态值}官方推荐小写
Route Group(分组)(任意,但建议 kebab)(marketing), (auth)不影响 URL括号内可随意,但保持一致
Private Folder_开头_components, _lib不生成路由官方支持,用于 colocated 文件
页面文件固定page.tsxlayout.tsx必须是这些文件名,不能改
组件文件(非路由)kebab-case 或 PascalCaseUserProfile.tsxuser-profile.tsx看团队偏好,组件名本身用 PascalCase

组件命名风格

可以使用kebab-case(小写 + 连字符)来命名,也可以使用PascalCase(大驼峰)来命名,看要求。

列表页,添加搜索、分页功能

NuQS 的核心就是 URL query state 管理器,分 Server 端和 Client 端使用场景

  1. Server Components / Route Handler

    • 使用 nuqs/server
    • 读取 query params → 页面渲染一次
    • Server Component 会根据 query 改变重新渲染(页面 SSR)
  2. Client Components

    • 使用 useQueryState + nuqs(注意不是 nuqs/server
    • 修改 query state 会触发组件重新渲染,但不会自动刷新整个页面
    • React Query / useEffect / table hook 可以监听变化,然后发请求刷新数据

重点:search params创建 + pagination + 具体的components解析search Params,必须保持全部是server side或者全部是client side。如果不能保证全部一致,则翻页不生效。这是我实际写的过程中得出的经验。

刚开始并不会写,于是叫AI帮我实现,但是AI没有将三个内容保持一致,所以实现不了,于是问AI有没有投诉nuqs不能实现翻页,AI grok说没有,倒是有一个中文用户投诉(我猜想这个人就是我),既然没有一个人认为nuqs使用起来有问题,那么肯定是我自己的问题。于是仔细看文档,看官方示例代码,同时下载一个github上的示例代码(nextjs + nuqs),发现它能够正常翻页,那么肯定是我自己的问题了。后面就发现了原来 search params创建 + pagination + 具体的components解析search Params,必须保持server或者client一致。

使用server components来实现

参考:https://nuqs.dev/docs/server-side

使用loader函数,在server端来创建和解析search params。

1、定义搜索参数,并创建服务器端加载器和搜索序列化

2、在server component里面解析并使用搜索参数

关键点:

3、pagination组件接收到当前的searchParams,然后根据searchParams做相应的处理

这里只有一个page参数,实际上可以有很多参数。我将page参数传递给pagination组件之后,就可以高亮显示当前页,并且将page应用到prevPage和nextPage的href上面,进行翻页。

重点需要注意的是:shadcn的PaginationPrevious、PaginationLink、PaginationNext组件上,需要指定href来进行跳转,这个href应该是完整的路由地址。我之前使用的是下面这样的方法,是不行的。

下面就是AI告诉我的方法,因为在server components里面不能使用usePaginationQuery来解析search params,所以AI另外使用createLoader来创建同样参数的解析器loadPagination, 可是usePaginationQuery和loadPagination之间根本就没有关系,二者之间的变化都不能传递到彼此,所以是完全没有作用的。就是GROK告诉我的错误答案。

4、那么其余的搜索参数该怎么处理呢?还是一样的,先创建一个组件,然后组件接收相应的搜索参数,使用form或者nextjs提供的Form组件来进行路由跳转。

比如说一个输入框搜索组件,可以这样写:

当然,后面可以将input单独抽离出来做成组件,还可以添加select、cascade select等等搜索组件。

5、需要注意的是,https://nuqs.dev/docs/server-side文档里面介绍了两种方法,但只需要使用createLoader方法即可,不要看到createSearchParamsCache就感觉有缓存会更牛逼,还是自己的英语水平不好,不能很好的理解意思

区别:

使用client components来实现

1、用 useQueryState 代替 React.useState,让状态同时保存在 URL 上。这样做的好处:

2、需要直接在pagination组件里面接收useQueryState函数的返回值,然后使用状态更新函数来更新search Params,这一点与server端是不同的

注意看,就是使用setPage来翻页的。

3、在组件里面使用usePage(也就是useQueryState函数的返回值)来获取searchParams和状态更新函数,传递给pagination组件。

添加better-auth登录功能

项目在prisma配置好之后,就要配置better-auth,因为里面有User表,要按照它的来做,所以对后面的很多表都有影响,最先来做是最好的。

Better-Auth 的核心思想是将认证逻辑(Auth Handler, Server)和用户界面逻辑(Auth Client, React Hooks)分离,并利用 Next.js 的路由和中间件进行集成。

我先把整个流程讲解一下,后面再讲解具体操作:

流程

1、数据库模型定义 (Prisma Schema Definition)

这是整个认证系统的基石,作用是告诉 Better-Auth 数据应该以何种结构存储

流程步骤文件示例作用
定义认证相关模型prisma/schema.prisma🌟 数据结构约定:你定义的 User, Session, Account, 和 Verification 四个核心模型,是 Better-Auth 适配器(Prisma Adapter)用来进行所有数据库操作的蓝图。它决定了用户的身份信息、登录状态、社交账号绑定(GitHub等)以及邮箱验证信息将如何与 PostgreSQL 交互和存储。

为什么它很重要?

Better-Auth 依赖于这些特定的模型来处理认证流程:

为什么按照它的方式来创建这些表呢?我想创建自己的User表不行吗?因为better-auth的原理是这样的:

Better-Auth 的核心实现原理是基于 Tokenless Session Management (无令牌会话管理),结合 Prisma 数据库适配器 来实现全栈身份验证。它遵循了现代 Web 应用中常见的 Session Cookie 机制

Better-Auth 的三大核心原理

1. Tokenless Session Management(无令牌会话管理)

Better-Auth 避免使用 JWT (JSON Web Tokens) 这种需要在每个请求中传输大量用户数据的传统方式。它的原理更接近于传统的基于 Cookie 的会话:

 

2. Prisma Adapter(数据库适配器)

Better-Auth 通过 prismaAdapter 将其认证流程与您的 PostgreSQL 数据库连接起来:

3. Next.js 集成机制

Better-Auth 利用 Next.js 的特点,实现服务端和客户端的无缝衔接:

简而言之,Better-Auth 的实现原理就是:利用安全的 Session Cookie 作为密钥,通过 Prisma 适配器连接到您的数据库,在服务器端进行身份验证和数据管理,并在 Next.js 的 Server/Client 组件中提供便捷的 Hooks 来消费这些认证状态。

2、后端配置与数据库集成 (Server Setup)

流程步骤文件示例作用
初始化 Better-Auth 实例lib/auth.ts🌟 定义认证规则:这是 Better-Auth 的核心,负责告诉系统如何工作。它配置了:1. 数据库适配器(如 Prisma),用于存储用户、Session 和 Account 数据。2. 认证策略(如 Email/Password, GitHub, Google),用于定义用户登录的方式。

3、API 路由集成 (API Handler)

流程步骤文件示例作用
创建认证 API 路由app/api/auth/[...all]/route.ts🌟 处理所有认证请求:这个 Catch-all 路由([...all])将所有发送到 /api/auth/* 的请求(如登录、注册、OAuth 回调、登出等)转发给 Better-Auth 的 Handler 进行处理。它将 Better-Auth 的核心逻辑暴露给 Next.js 应用程序。

因为打开页面的时候,nav或header上面的auth-user-menu组件会使用useSession()来查看是否登录了,所以会触发/api/auth/get-session接口调用,这个是better-auth帮我们做的。还有登录、登出的接口,都是这样的形式:/api/auth/xxxxxxxx,所以这里就需要有这个文件,让触发这些接口的时候,转交给better-auth进行处理。

4、前端客户端配置 (Client Setup)

流程步骤文件示例作用
创建 Auth Client 实例lib/auth-client.ts🌟 封装前端交互方法:基于后端配置,创建一个客户端实例。它导出了供 React 组件使用的便捷方法,例如 signIn, signUp, signOut 以及最重要的 useSession Hook。
登录、登出、注册、查看是否登录,用到的就是这些方法。这里配置好了之后,在各个组件里面引入使用即可,非常方便。

5、前端组件与会话管理 (Usage & Session Management)

流程步骤文件示例作用
组件中使用 Hookapp/page.tsx (Client Component)🌟 管理用户状态和 UI 切换:通过 useSession() Hook,组件可以获取当前用户的登录状态、用户信息(session.user)和加载状态。这是实现“登录/登出”按钮切换、显示用户名称、以及在客户端保护路由的基础。
服务端获取 Sessionapp/dashboard/page.tsx (Server Component)🌟 在服务端渲染时获取用户信息:在 Next.js 13/14 的服务端组件中,可以直接调用 auth.api.getSession(),通过读取 Request Headers 中的 Cookie 来预先获取会话信息,实现高效的 SSR 或 SSG。

6、编写登录界面

上面的1-4步做好之后,其余的都好说了。

①创建一个登录组件,就是显示登录、登出的button样式的组件,放到nav栏上。里面使用useSession()来查看是否已登录。如果点击登录,就跳转到/sign-in页面;如果点击登出,就调用auth-client里面暴露的signOut方法即可。

②编写app/(auth)/sign-in/page.tsx页面,这个页面UI按照需求编写即可,完全可以是一个普通的form页面。重点是提交表单的时候,要调用auth-client暴露的signIn方法来登录。

③在sign-in页面中,可以加一个跳转链接,跳转到app/(auth)/sign-out/page.tsx页面,这个页面UI也是一个普通的form页面。重点是调用auth-client暴露的signUp方法来注册。

操作

参考https://www.better-auth.com/docs/installation,但是没有专门的nextjs相关的,所以需要结合AI来做。

1、安装依赖npm install better-auth

2、设置环境变量

.env里面添加:

如果需要添加github登录,需要获取GITHUB OAuth凭证后添加:

GitHub Developer Settings -> New OAuth App。

Homepage URL: http://localhost:3000

Authorization callback URL: http://localhost:3000/api/auth/callback/github

获取 Client ID 和 Client Secret。

3、创建better-auth实例,配置数据库及prisma适配器

创建/app/lib/auth.ts文件。加入 邮箱密码、GitHub 登录支持和 Prisma 适配器配置。

4、创建better-auth相关的数据库

先安装依赖pnpm add -D @better-auth/cli,这个依赖是用来生成ORM schema的。

然后使用npx @better-auth/cli generate命令来生成model,这个会在现有的schema.prisma里面追加model,可以根据项目需要添加一些字段,但生成的字段最好全部保留。

上面这个方式默认使用的是sqlite3,与postgresql冲突。那么不使用这种方式,想直接定义model,可以参考:https://www.better-auth.com/docs/concepts/database#core-schema,按照里面来定义即可。下面的供参考,还是要检查一下有没有变化:

5、API路由集成

创建/app/api/auth/[...all]/route.ts文件,将代码copy进去即可。这个文件就是帮助我们处理better-auth相关的接口。

image-20251202132705481

6、创建better-auth客户端实例

client实例,可以帮助我们与auth服务进行交互,实例上的signIn方法可以帮助我们登录,signOut方法可以帮助我们登出,useSession方法帮助查询是否已登录等等。

这样我们就不用自己定义API接口了,直接使用这些方法即可。

7、编写登录功能

better-auth客户端实例的方法,使用范例在这里:https://www.better-auth.com/docs/basic-usage

①创建一个登录组件,就是显示登录、登出的button样式的组件,放到nav栏上。里面使用useSession()来查看是否已登录。如果点击登录,就跳转到/sign-in页面;如果点击登出,就调用auth-client里面暴露的signOut方法即可。

②编写app/(auth)/sign-in/page.tsx页面,这个页面UI按照需求编写即可,完全可以是一个普通的form页面。重点是提交表单的时候,要调用auth-client暴露的signIn方法来登录。

③在sign-in页面中,可以加一个跳转链接,跳转到app/(auth)/sign-out/page.tsx页面,这个页面UI也是一个普通的form页面。重点是调用auth-client暴露的signUp方法来注册。

因为nextjs只能做一些简单项目,所以不要担心更复杂的权限,先把这种权限掌握好即可。

light和dark主题转换

使用next-themes库。设置一个 ThemeProvider 组件来包裹您的应用,并使用 useTheme 钩子来切换主题。这个库能自动处理系统偏好设置,并防止页面闪烁。

1、首先在app/layout.tsx里面添加ThemeProvider

image-20251202104505015

2、编写一个toggle-mode组件,用来切换主题

image-20251202104628126

3、在nav或header上面添加toggle-mode组件,因为这个组件使用了Provider,所以看上去是在别处操作,但是会作用到全局。这就是Provider的好处。

image-20251202104740669

疑问:next-themes是怎么设置dark mode的呢?

next-themes 的核心功能是根据当前选择的主题(例如 'light', 'dark', 或 'system')来动态地给根 HTML 元素 (<html>) 添加或移除一个 CSS class。

获取数据时,怎么处理loading和error

这里只谈论GET请求,因为create、update、delete这些请求都会使用server actions来处理,里面会用到useActionState,处理loading和error就比较简单。

我会经常写这样的代码:

那么这里的数据库请求,如果报错了,或者接口很慢,我该怎么处理?

1、使用loading.tsx来处理loading

这是全页面的效果,如果这个页面很大,而页面中别的部分在loading时可能看不到了,所以使用范围有局限。就算使用骨架屏效果,还是不好。

2、使用Streaming + 局部 Suspense来处理loading

这是nextjs v16最推荐的方式,处理的粒度很细。为需要的地方做一个骨架屏效果即可。

3、使用try...catch + error.tsx来处理错误

在获取数据时,使用try...catch来处理可能出现的错误,然后使用error.tsx来展示错误和重试。

但是建议只在当你需要对某些特定错误做精细处理时才写try...catch,普通错误交给error.tsx来处理即可。这是因为 Next.js 官方有意把“严重错误”(网络断开、数据库挂了、代码 bug、超时等)统一交给 error.tsx 处理。还是看项目要求,不要想太多。

nextjs里面组件间传参的方式有哪些?

1、URL传参

① params传参

场景:每个条目都有独立页面、需要 SEO。对应的是url上的params参数:/products/productId,就是这里的productId

② searchParams传参

场景:筛选/分页/排序/临时状态。对应的是url上的query参数:?page=3&sort=price-desc

2、props传参

简单数据、复杂数据都可以通过这种方式来传递。

一般的用法是这样的:

但是如果使用了Suspense,那么就可以使用use来解包Promise。这种是v16时代经典。

3、通过Context传参

这个经常使用,读取和操作数据非常方便。但是只能在client components里面使用。

4、zustand、redux等第三方库传参

这个暂时用不到。

新增、编辑怎么做

项目结构

可以使用简单的modal来做,但是最标准的做法就是做成页面。那么项目结构是什么样的呢?

因为新增、编辑页面大部分内容都是一样的,所以需要提取一个公用的表单组件:

实现方式:

一次成功的项目部署

将项目部署到vercel上,其实很简单。有几点需要注意:

1、prisma generate生成的内容,需要上传到git。如果在gitignore里面忽略掉了,需要删除忽略项。

2、如果使用了better-auth,那么需要在github OAUTH APP里面,既生成链接为localhost:3000的app,也要生成网站上线后的网址为链接的app。等于说是要生成2个。可以都放到.env里面去管理。

image-20260330142631321

并且上线后的系统变量里面的BETTER_AUTH_URL=https://atlove.top,要改为具体的网址。

3、如果使用的是具体的安装包的工具,比如说想明确是npm、yarn还是pnpm,那么在部署的时候,要指定具体的运行命令。

4、如果想要绑定域名,那么就需要在settings里面,先添加域名。

image-20260330143039890

image-20260330143110103

image-20260330143130508

添加了域名还不够,还需要将这个域名绑定到cloudflare上面去。

image-20260330143339101

image-20260330143351308

在不同的浏览器上保持字体一致

我使用的是nextjs项目创建时,默认的layout.tsx的内容,里面的body的className上指定的是默认的内容,但是在chrome和firefox上的字体就明显不同,因为我一直是在chrome上进行调试的,所以没有发现这个问题,等到后面上线了,我在firefox上打开博客,发现字体不是我想要的效果。

image-20260331102352997

image-20260331102400990

为什么 Chrome 和 Firefox 表现不同?

原因:

虽然你在 body 上添加了 ${geistSans.variable},但这只是定义了一个 CSS 变量(--font-geist-sans),并没有告诉浏览器“整个页面都要使用这个字体”。

 

解决方法:

修改 RootLayout,将 geistSans.className 加入到 body 中:

如何让静态页面,在进入时重新渲染

我在做博客项目的时候,about页面有动画,我想要的效果是每次进入这个页面都会显示动画。但是只有第一次进入时显示了动画。

这通常是因为 Next.js 的客户端路由缓存(Client-side Cache)机制 导致的。

当你第一次进入 /about 页面时,Next.js 会完整挂载该组件,从而触发 CSS 或 Framer Motion 定义的入场动画。但当你点击 Header 里的 Link 切换到其他页面再切回来时,Next.js 会尽量重用已经加载的组件实例以提升性能,导致组件没有经历完整的“销毁 -> 重新挂载”过程,动画自然就不会再次触发。

解决方法:

为根容器添加唯一的 key

这是 Next.js 中强制触发重绘最简单的方法。通过给 About 页面的外层容器绑定 usePathname,让 React 认为“路径变了,这是一个全新的组件”。

 

博客项目,fitness的routines更新了,但是timer-circle显示不对

现象:

我的后台数据fitness更新后,routines数据更新了(也就是setRoutines正常),但是好像zod里面的数据没有更新。现象是timer-circle里面的一圈还是5分钟,实际数据是15分钟,这样倒计时运行的时候,5分钟就转了一圈,总共转了3圈。运行了第一个EXERCISE之后卡死了,显示init routines,按F5刷新之后就好了。

 

为什么会造成这种现象?

我在fitness action里面,都有这样的代码:

为什么数据还是有问题呢?

revalidatePath / revalidateTag 的作用范围有限

所以,涉及到zustand数据更新的地方,需要手动进行操作。

 

总结一句话: revalidatePathrevalidateTag 只能让下一次服务器请求拿到新数据,但无法清除客户端已经持久化在 localStorage 里的 Zustand 状态。这就是你改完后台后不刷新就出问题的核心原因。

 

比如说我的第一个exercise里面的时间,之前是900s。然后在后台我将其改为了75s,回到fitness页面之后,返回的routines数据是更新了,但是activeRoutine里面的steps数据没有更新。

image-20260428094100376

 

解决办法

在RoutinesInitializer里面,我是这样判断的:如果没有activeRoutineId,那么就设置活动routine。但是后台更新之后,activeRoutineId没有变化,还是缓存里面的数据。

顺便说一下,这里的routines,在后台数据更新后,拿到的是最新的数据,所以和cache components及其缓存是没有关系的。

我想到的解决办法是:在setRoutines时,判断后台返回的routines数据和缓存里面的数据是不是一样的,如果是一样的,那么直接赋值;如果不是一样的,那么就设置activeRoutineId为null,这样就可以更新setActiveRoutine了。

判断数据是否一致,不建议使用JSON.stringify()方法,因为性能很差。这里使用数组的some方法,只要有一个为true,就会返回true,性能更好。

所以说,遇到问题,最好还是先找自己代码的问题,不要动不动就说nextjs框架怎么样,怎么不好用。说不定就是自己代码有问题,考虑不周。