尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

React主流库全解析:路由、状态管理与数据请求实战

React主流库全解析:路由、状态管理与数据请求实战 React 的“主流库”不像表面上那么少。很多人一聊 React 生态脑子里只有 React Router 和 Redux但实际上常用的路由、状态管理、数据请求、表单、UI 组件、动画、跨端框架加起来至少有 10 个以上。这次我们把 React 生态里最常用的主流库全部过一遍不做概念堆砌直接看每个库负责什么、怎么选、怎么在项目里落地以及最容易踩的坑。内容包括核心能力速览、工程化环境准备、路由实战、数据请求方案、状态管理选型、表单处理、组件库和动画方案最后给出一套可复制的排查思路和使用建议。如果你正在做 React 技术选型、准备上手 React 生态或者想把老项目里的方案换掉这篇文章可以直接收藏。1. React 主流库核心能力速览React 本身是视图层框架真正让项目跑起来的业务能力基本都来自周边库。按照用途划分可以把 React 主流库分成下面几类能力方向主流库/方案主要解决的问题核心特点路由React Router页面切换、URL 与组件映射声明式路由支持嵌套路由、路由守卫、懒加载状态管理Redux Toolkit、Zustand、Jotai跨组件共享状态、全局状态持久化Redux 适合大型项目Zustand 轻量Jotai 原子化数据请求TanStack Query、Axios、SWR服务端状态、请求缓存、重试、分页Query 自动缓存/去重适合接口密集型页面表单React Hook Form、Formik表单状态、校验、错误处理RHF 性能好配合 Zod 可做类型安全校验UI 组件库Ant Design、Material UI、Arco Design企业级后台组件、表单组件、表格开箱即用适合中后台快速开发样式方案Tailwind CSS、CSS Modules、styled-components样式组织、主题切换、样式隔离Tailwind 原子类CSS Modules 局部作用域动画Framer Motion、React Spring页面转场、元素动画、手势交互声明式 API配合 React 状态驱动动画框架Next.js、Remix全栈 React、SSR、路由、服务端数据获取适合 SEO 场景和全栈项目跨端React Native、Expo移动端 App一套代码运行 Android/iOS组件测试Jest、React Testing Library、Vitest组件单元测试、交互测试RTL 强调用户行为测试从选型角度看一套常见的中后台技术栈可能是Vite React Router Zustand TanStack Query React Hook Form Ant Design Tailwind CSS。一套面向内容站点或全栈应用的技术栈可能是Next.js TanStack Query Tailwind CSS Framer Motion。面向移动端则是 React Native 或 Expo 起步。这里要强调一个原则没有“最好”的库只有当前项目阶段最合适的组合。后面每个章节会说明不同方案的取舍和适用边界。2. 适用场景与使用边界React 主流库覆盖面很广但不同场景的选型思路差别很大。先想清楚项目类型再决定要不要引入某个库。2.1 适合的场景中后台管理端是 React 生态最强的场景。这类项目页面结构规整、表单和表格密集、路由层级固定非常适合用 React Router Ant Design React Hook Form TanStack Query 的组合。开发速度明显快于从零手写组件。内容型网站和需要 SEO 的页面应该直接考虑 Next.js。因为纯客户端渲染的 React 项目对搜索引擎不友好而 Next.js 的服务端渲染和静态生成可以解决这个问题。移动端开发则有 React Native 和 Expo 路线。如果团队已经熟悉 React用 RN 做跨端方案代码复用率比单独维护两套原生项目要高很多。2.2 不适合的场景不是所有项目都需要重量级状态管理库。如果页面状态基本是组件内部状态用 Redux 反而增加代码量和心智负担。小型工具页、单页展示页直接用 useState useReducer 就足够了。动画库也不是必须上。如果只是简单的 CSS 过渡用 transition 和 animation 就可以。Framer Motion 适合复杂动画和交互动效但不适合为了“炫技”而引入一个 50KB 以上的运行时依赖。2.3 版权、安全与合规边界React 本身是 MIT 协议主流周边库大多也是 MIT 或 Apache 2.0 协议可以用于商业项目。但要注意两点第一使用组件库和开源代码时保留原协议声明第二在项目里引入第三方接口和依赖时不要随意绕过权限校验。涉及用户数据、登录态的页面必须在服务端做鉴权不能只靠前端路由守卫。3. React 项目环境准备与工程初始化主流库的落地需要先有一个可运行的 React 工程。现在推荐用 Vite 来初始化而不是老的 Create React App。# 使用 Vite 创建 React TypeScript 项目 npm create vitelatest my-react-app -- --template react-ts cd my-react-app npm install npm run dev环境要求并不复杂Node.js 建议使用 18 以上版本包管理器可以选择 npm、pnpm 或 yarn。pnpm 在依赖安装速度和磁盘占用上更优适合 monorepo 和大型项目。# 常用依赖安装命令按项目需要选择 npm install react-router-dom npm install zustand npm install tanstack/react-query npm install axios npm install react-hook-form zod npm install antd ant-design/icons npm install tailwindcss tailwindcss/vite npm install framer-motion建议工程结构按功能模块划分目录src/ api/ # 接口请求封装 components/ # 全局通用组件 features/ # 业务模块 hooks/ # 通用 Hooks layouts/ # 布局组件 router/ # 路由配置 stores/ # 状态管理 types/ # 类型定义 utils/ # 工具函数 App.tsx main.tsx这种结构的好处是路由、状态、接口层职责清晰后面接入任何一个库都有明确的放置位置。初始化阶段最容易遇到的问题就是依赖版本冲突所以建议使用 pnpm 锁定依赖版本或者使用 package.json 里的 lock 文件统一版本。4. 路由方案React Router 实战React Router 是目前 React 生态里使用率最高的路由库。日常开发中主要涉及基础路由配置、嵌套路由、懒加载、路由守卫四类能力。4.1 基础路由与嵌套路由新版 React Router 使用createBrowserRouter来配置路由表。以下是一个中后台项目常用结构// router/index.tsx import { createBrowserRouter, Navigate } from react-router-dom import Layout from /layouts/AdminLayout import Dashboard from /features/dashboard import UserList from /features/user/UserList import UserDetail from /features/user/UserDetail import Login from /features/login const router createBrowserRouter([ { path: /login, element: Login / }, { path: /, element: Layout /, children: [ { index: true, element: Navigate to/dashboard replace / }, { path: dashboard, element: Dashboard / }, { path: user, children: [ { index: true, element: UserList / }, { path: :id, element: UserDetail / } ] } ] } ]) export default router入口组件中配合RouterProvider加载路由// main.tsx import { RouterProvider } from react-router-dom import router from ./router createRoot(document.getElementById(root)!).render( RouterProvider router{router} / )嵌套路由最直接的收益是父布局复用。侧边栏、头部导航共享一次子页面只需要承担内容区渲染代码可维护性好很多。4.2 懒加载大型项目路由按页面拆包可以减少首屏体积。import { lazy, Suspense } from react const Dashboard lazy(() import(/features/dashboard)) const UserList lazy(() import(/features/user/UserList)) Suspense fallback{PageLoading /} Dashboard / UserList / /Suspense懒加载之后每个路由页面的 JavaScript 会按访问顺序请求而不是首屏一次全拉下来。首次访问某页面时会有短暂的加载态需要准备统一的 Loading 组件。4.3 路由守卫React Router 没有内置“路由守卫”概念但可以通过封装高阶组件或组件包裹实现。登录态判断是常见场景// components/AuthGuard.tsx import { Navigate, Outlet } from react-router-dom import { useAuthStore } from /stores/auth export default function AuthGuard() { const token useAuthStore((state) state.token) if (!token) { return Navigate to/login replace/ } return Outlet/ }然后在路由表中给需要权限的父路由包一层 AuthGuard{ path: /, element: AuthGuard /, children: [ { path: dashboard, element: Dashboard / } ] }需要明确的是前端路由守卫只是用户体验层面的限制真正的数据权限必须在接口服务端确认否则绕过前端直接请求接口依然可能拿到数据。5. 状态管理Redux Toolkit、Zustand 与 Jotai状态管理是 React 项目最容易过度设计的地方。这里按照项目规模给出三种方案的选型建议。5.1 Redux Toolkit大型中后台的默认选择Redux Toolkit 是 Redux 官方推荐写法内置了 createSlice、configureStore、createAsyncThunk代码量比老 Redux 少很多。// stores/user.ts import { createSlice, createAsyncThunk } from reduxjs/toolkit import { fetchUserList } from /api/user export const getUserList createAsyncThunk( user/getList, async (params: ListParams) { return await fetchUserList(params) } ) const userSlice createSlice({ name: user, initialState: { list: [], loading: false }, reducers: { clearList(state) { state.list [] } }, extraReducers(builder) { builder .addCase(getUserList.pending, (state) { state.loading true }) .addCase(getUserList.fulfilled, (state, action) { state.loading false state.list action.payload }) } }) export const { clearList } userSlice.actions export default userSlice.reducerRedux Toolkit 适合多模块共享状态、有复杂业务流转、需要时间旅行调试的场景。它的缺点是样板代码仍然偏多小团队小项目会有“写半天状态、写半天页面”的感觉。5.2 Zustand轻量、直接、可替换Zustand 是目前口碑较好的轻量状态库。API 非常精简不需要 Provider 包裹直接在组件外创建 store组件里用 hook 消费// stores/useCartStore.ts import { create } from zustand interface CartState { items: CartItem[] total: number addItem: (item: CartItem) void removeItem: (id: string) void } export const useCartStore createCartState((set) ({ items: [], total: 0, addItem: (item) set((state) ({ items: [...state.items, item], total: state.total item.price })), removeItem: (id) set((state) ({ items: state.items.filter((item) item.id ! id), total: state.total - (state.items.find((item) item.id id)?.price ?? 0) })) }))组件里使用const items useCartStore((state) state.items) const addItem useCartStore((state) state.addItem)Zustand 非常推荐用于电商购物车、主题切换、登录态、多步骤表单这类场景。它有浅比较机制选择器的写法可以让组件避免不必要的渲染。Zustand 还支持持久化中间件可以把 token 等状态存到 localStorage。5.3 Jotai原子化状态Jotai 的设计思路是把状态拆成原子。如果说 Zustand 是把状态集中在一个 storeJotai 就是一个一个独立的小状态。适合状态间依赖关系简单、希望按组件维度拆分状态的场景。import { atom, useAtom } from jotai const themeModeAtom atomlight | dark(light) const userInfoAtom atomUserInfo | null(null) function ThemeSwitch() { const [mode, setMode] useAtom(themeModeAtom) return button onClick{() setMode(mode light ? dark : light)} / }Jotai 对 TypeScript 支持友好状态定义和消费都在组件附近适合中小项目或者想把状态“原子化”拆开的团队。6. 数据请求方案TanStack Query 与 Axios大部分 React 项目的接口数据来自远端这部分状态属于“服务端状态”和组件状态、业务状态不是一回事。服务端状态管理的核心问题包括缓存、Loading 态、错误重试、分页、增量更新。TanStack Query 就是解决这个问题的。6.1 QueryClient 配置先创建一个 QueryClient并在应用根部包裹// main.tsx import { QueryClient, QueryClientProvider } from tanstack/react-query const queryClient new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 1000, retry: 2 } } }) createRoot(document.getElementById(root)!).render( QueryClientProvider client{queryClient} App / /QueryClientProvider )staleTime 决定了数据在多久内认为是新鲜的过期的数据在组件挂载时后台重新请求。retry 控制失败后的重试次数。合理配置 staleTime 能显著减少无意义的重复请求。6.2 useQuery 与 useMutation列表页请求使用 useQuery// features/user/useUsers.ts import { useQuery } from tanstack/react-query import { fetchUserList } from /api/user export function useUsers(params: ListParams) { return useQuery({ queryKey: [users, params], queryFn: () fetchUserList(params), placeholderData: keepPreviousData }) }组件使用const { data, isLoading, isError, refetch } useUsers({ page: 1, size: 20 })新增、编辑、删除这类写操作使用 useMutationimport { useMutation, useQueryClient } from tanstack/react-query const queryClient useQueryClient() const createUserMutation useMutation({ mutationFn: (payload: CreateUserPayload) createUser(payload), onSuccess: () { queryClient.invalidateQueries({ queryKey: [users] }) } })onSuccess 里 invalidateQueries 会让旧的列表缓存失效并自动重新拉取。这是 TanStack Query 和手写请求逻辑最大的区别写操作成功之后数据可以自动同步。6.3 Axios 请求封装Axios 更多是作为请求执行器和拦截器使用。常见的做法是统一封装一个 request 实例// api/request.ts import axios from axios import { message } from antd const request axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 15000 }) request.interceptors.request.use((config) { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( (response) response.data, (error) { message.error(error.message || 请求失败) return Promise.reject(error) } ) export default request实际操作中Axios 负责请求发送和错误处理TanStack Query 负责缓存和状态管理。两者配合代码结构很清晰。7. 表单方案React Hook Form ZodReact 表单最大的痛点是受控组件一多、触发重渲染频繁以及校验逻辑放得杂乱。React Hook Form 通过非受控方式减少渲染次数Zod 提供类型安全的校验规则。7.1 基础表单// features/login/LoginForm.tsx import { useForm } from react-hook-form import { zodResolver } from hookform/resolvers/zod import { z } from zod const loginSchema z.object({ username: z.string().min(4, 用户名至少 4 位), password: z.string().min(6, 密码至少 6 位) }) type LoginFormData z.infertypeof loginSchema export default function LoginForm() { const { register, handleSubmit, formState: { errors } } useFormLoginFormData({ resolver: zodResolver(loginSchema) }) const onSubmit (data: LoginFormData) { console.log(data) } return ( form onSubmit{handleSubmit(onSubmit)} div input {...register(username)} placeholder用户名 / {errors.username span{errors.username.message}/span} /div div input typepassword {...register(password)} placeholder密码 / {errors.password span{errors.password.message}/span} /div button typesubmit登录/button /form ) }register 方法把 input 注册给 RHF 管理不需要手动维护每个字段的 value 和 onChange。校验错误通过 formState.errors 取到。7.2 与 Ant Design 集成React Hook Form 和 Ant Design 结合时需要把 AntD 组件的 onChange 交给 RHF 控制import { Controller, useForm } from react-hook-form const { control, handleSubmit } useFormFormData() Controller namerole control{control} render{({ field }) ( Select value{field.value} onChange{field.onChange} options{[ { value: admin, label: 管理员 }, { value: user, label: 普通用户 } ]} / )} /Controller 的作用是把自定义组件桥接到 RHF。有了 Controller不管是 Select、DatePicker、Radio 还是 Upload都能用同一套状态和校验体系管理。表单库选型时React Hook Form 是最推荐的新方案。Formik 虽然曾是行业标准但样板代码偏多新项目建议直接 RHF。8. UI 组件库与样式方案8.1 组件库Ant Design 仍是中后台首选Ant Design 在中后台领域的组件覆盖度非常高表格、表单、弹窗、消息提示、上传、日期选择都有成熟实现。使用方式import { Button, Table, Space, Modal } from antd import { PlusOutlined } from ant-design/icons Space Button typeprimary icon{PlusOutlined /}新建/Button Button danger删除/Button /SpaceAntD 在实际项目中建议配合主题定制。使用 Vite 时可以通过 ConfigProvider 统一设置主题 tokenimport { ConfigProvider } from antd ConfigProvider theme{{ token: { colorPrimary: #1677ff } }} App / /ConfigProvider8.2 样式方案Tailwind CSS 与 CSS ModulesTailwind CSS 是实用性优先的原子类方案。在 Vite 项目中安装npm install tailwindcss tailwindcss/vite然后配置// vite.config.ts import tailwindcss from tailwindcss/vite export default defineConfig({ plugins: [react(), tailwindcss()] })写入入口 CSSimport tailwindcss;使用方式div classNameflex items-center justify-between p-4 bg-white radius-lg shadow-sm span classNametext-sm font-medium text-gray-800用户列表/span Button typeprimary新增用户/Button /divTailwind 适合和组件库配合使用AntD 管核心业务组件Tailwind 管布局和自定义样式。不需要在 CSS 文件里给每个布局起名字。如果团队习惯了 CSS Modules 或 styled-components也不影响它们各自都能实现样式隔离和主题管理。9. 动画方案Framer Motion页面切换、卡片入场、数字滚动、手势拖拽这些交互用 CSS 写起来繁琐用 Framer Motion 会更直观。import { motion, AnimatePresence } from framer-motion motion.div initial{{ opacity: 0, y: 20 }} animate{{ opacity: 1, y: 0 }} exit{{ opacity: 0, y: -20 }} transition{{ duration: 0.3 }} content /motion.div路由切换动画结合 AnimatePresence 可以实现页面进出场AnimatePresence modewait motion.div key{location.pathname} Outlet/ /motion.div /AnimatePresence需要注意动画库是有运行时开销的。轻量页面使用没有问题但列表页大量元素同时执行动画时会出现卡顿。生产环境建议只对首屏关键元素加动画不要每个列表项都套 motion。10. 跨端与框架Next.js、React Native10.1 Next.js全栈 React 与服务端渲染如果项目需要 SEO、服务端渲染或者做完整全栈应用Next.js 是 React 生态里最主流的框架。它内置了文件路由、API 路由、服务端组件、图片优化和静态生成能力。// app/page.tsx export default async function HomePage() { const res await fetch(https://api.example.com/posts) const posts await res.json() return ( div {posts.map((post) ( div key{post.id}{post.title}/div ))} /div ) }App Router 下服务端组件可以直接在组件内发起请求不需要额外的 useEffect。这个能力对内容站特别友好。10.2 React Native 与 Expo移动端跨平台React Native 是 React 语法在移动端的延伸。Expo 则进一步降低了 RN 的开发门槛可以用手机 App 扫码预览、一键打包。import { View, Text, StyleSheet } from react-native export default function HomeScreen() { return ( View style{styles.container} Text style{styles.title}React Native Home/Text /View ) } const styles StyleSheet.create({ container: { flex: 1, justifyContent: center, alignItems: center }, title: { fontSize: 24, fontWeight: 600 } })RN 适合需要同时上架 Android 和 iOS 的团队。如果只是内部工具也可以考虑 Web 项目直接响应式适配不一定需要引入 RN。11. 接口能力与批量任务的工程化组织React 项目中的“接口能力”和“批量任务”指的是接口层如何统一管理、页面大量并发请求如何控制、重复请求如何避免。11.1 接口层统一管理建议每个业务域的接口独立成文件方便复用和维护// api/user.ts import request from ./request export const fetchUserList (params: ListParams) request.get(/api/user/list, { params }) export const createUser (payload: CreateUserPayload) request.post(/api/user/create, payload) export const updateUser (id: string, payload: UpdateUserPayload) request.put(/api/user/${id}, payload) export const deleteUser (id: string) request.delete(/api/user/${id})11.2 批量请求与并发控制React 客户端通常会遇到表格勾选批量删除、批量导出、大量明细加载等场景。这些操作如果一次发几十个请求很容易把浏览器同时请求数打满。推荐方案是服务端提供批量接口前端只发一次请求。如果服务端只有单条接口前端可以用 p-limit 之类的库做并发控制import pLimit from p-limit const limit pLimit(5) const tasks ids.map((id) limit(async () { await deleteUser(id) }) ) await Promise.all(tasks)TanStack Query 的 useQueries 也可以管理一组动态查询import { useQueries } from tanstack/react-query const userQueryResults useQueries({ queries: ids.map((id) ({ queryKey: [user, id], queryFn: () fetchUserDetail(id) })) })这种方式适合页面渲染前需要同时拉取多个详情的数据场景。12. 资源占用与性能观察React 项目不涉及显存问题但“资源占用”更多体现在包体积和运行时性能上。下面列出具体的观察方法。12.1 包体积用 Vite 构建时可以用rollup-plugin-visualizer看产物构成npm install -D rollup-plugin-visualizer// vite.config.ts import { visualizer } from rollup-plugin-visualizer export default defineConfig({ plugins: [react(), visualizer({ open: true })] })构建后会自动打开一个体积报告页面能看到哪些依赖占的空间大。常见处理办法AntD 按需加载、组件库走 ESM、大模块动态 import。12.2 运行时性能React 页面性能观察主要用 React DevTools 的 Profiler记录一个交互过程检查哪个组件渲染耗时高。如果某个组件在输入时整体重渲考虑是否把表单状态拆分细一点。列表项用 memo 包裹避免父组件更新时子项全部重渲。长列表用 react-window 或 react-virtualized 做虚拟滚动。TanStack Query 通过 staleTime 和 queryKey 设计可以减少重复请求。Zustand 通过选择器避免状态未变但组件重渲的问题。React Hook Form 通过非受控组件保证表单输入不触发页面级刷新。这些都是实际项目中常用到的性能优化点。13. React 主流库常见问题与排查问题现象可能原因排查方式解决方案npm install 报 ERESOLVE 依赖冲突依赖版本不兼容查看报错中的依赖链使用 pnpm 或更新版本React Router 页面刷新后 404服务器未配置 SPA fallback观察刷新前后 URL配置 rewrite 到 index.html路由切换后页面不更新router key 未绑定 location检查 AnimatePresence 或组件缓存给内容区 key 绑定 pathname状态更新了但组件不渲染Zustand 选择器返回新对象检查选择器返回值使用深度等值或拆细选择器TanStack Query 数据不更新staleTime 太长生成了缓存检查请求是否发送invalidateQueries 或调 short staleTimeReact Hook Form 校验不触发schema 字段名和组件 name 不一致对比字段名和 zod schema统一字段名AntD 主题不生效ConfigProvider 未包裹检查 App 根组件用 ConfigProvider 包住整个应用动画列表卡顿太多 motion 元素同时渲染DevTools Performance 录制减少动画元素、改用 CSS 动画Next.js 服务端请求失败服务端无法访问内网接口查看服务端日志将接口地址改为内网可访问地址React Native 启动白屏Metro 未启动或依赖没装查看 Metro 终端日志重启 Metro 并重新安装依赖这些问题的共性规律是先看终端日志再看网络面板最后看状态工具。React 生态的报错信息通常比较明确React DevTools 会直接提示组件更新链路上的问题。对于接口问题要区分是前端没发请求、请求发出但响应异常、还是响应后状态未更新这三种情况。14. 最佳实践与使用建议14.1 小型项目不要一上来就全家桶一个小型工具页只引入 React Router 和 axios 就够了。状态用 useState 或 useReducer不需要 Zustand更不需要 Redux Toolkit。表单不复杂时原生表单完全满足需求。14.2 中大型项目固定一套技术基线中大型中后台项目建议固定Vite TypeScript React Router Zustand TanStack Query React Hook Form Zod Ant Design Tailwind CSS。这套组合经过大量项目验证开发效率和可维护性都平衡得比较好。14.3 接口层和状态层分离请求封装只放在 api 目录不要在组件里直接写 axios。TanStack Query 的 queryKey 要规范统一这样可以复用缓存和失效逻辑。Zustand 只放真正的全局状态页面内部临时状态不要塞到全局 store。14.4 设计系统先行如果项目对 UI 一致性要求高建议先确定使用哪个组件库和哪套样式方案。AntD 的主题 token 可以统一改色Tailwind 的配置文件可以约束间距、字体、颜色。等页面开发到后期再换主题改动成本会高很多。14.5 每次升级 React 主版本时先检查周边库兼容性React 版本升级可能影响 React Router、Redux、相关 TypeScript 类型定义甚至部分 Hook 行为。升级前先看每个库的 release note改完跑一遍类型检查和全量测试。14.6 安全和合规提醒在 React 项目中前端路由和页面展示可以控制但数据安全必须由后端保证。任何涉及用户隐私或版权素材的内容都要确认数据来源合法、展示权限正确。组件库和开源代码在商业项目中使用时保留开源协议声明避免协议风险。15. 总结React 生态的主流库并非越全越好。路由、状态管理、数据请求、表单、UI、动画、框架和跨端方案每类库都有明确的适用场景和取舍。最推荐的第一步是先跑通一个 Vite TypeScript 的 React 项目然后在此基础上逐个引入 React Router、TanStack Query、Zustand、React Hook Form 和 Ant Design用一套常见的中后台页面把链路走通。这个过程基本能覆盖大部分 React 项目的日常开发内容。最容易踩的坑集中在三个地方一是状态管理选型过重项目不大却引入 Redux二是服务端状态和客户端状态混在一起接口数据不放 TanStack Query 而塞进全局 store三是不看依赖版本直接升级导致 React Router 或 AntD 类型报错。把这些点避开React 项目的主流程会顺畅很多。后续如果要扩展可以从 Next.js 服务端渲染、React Native 移动端、Framer Motion 动效设计和 TanStack Query 高级缓存策略这几个方向继续深入。
返回列表