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

资讯详情

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

React生态主流库选型指南:路由、状态管理与数据请求实战解析

React生态主流库选型指南:路由、状态管理与数据请求实战解析 1. React 真正的学习门槛不在 API而在生态选型很多人在学完 React 基础语法之后会陷入一个很真实的困境useState、useEffect、props都看懂了官方文档的示例也能照抄但一打开 GitHub 上一个真实项目立刻发现自己变成了“文盲”。createBrowserRouter、configureStore、useQuery、React Hook Form、Zod、Tailwind……每个文件里都有没见过的库每个库都有自己的 API全部叠加在一起远远超出新手的知识范围。这不是你的问题而是 React 生态的客观现状。React 本质上只是一个 UI 库官方只负责组件渲染和局部状态管理。它没有内置路由、没有内置数据请求方案、没有全局状态管理、没有表单校验、没有 UI 组件库。这些问题全部交给了社区解决。而社区为了应对不同业务场景给出了多种思路迥异的方案。于是React 真正的学习成本其实不在语法而在选型。这篇文章讲清楚一件事React 生态里那些“主流库”分别解决什么问题它们的核心心智模型是什么什么场景选哪个更合适。如果你正在学 React、准备面试、或者刚开始接手一个 React 项目这篇文章可以作为一张生态地图帮你把散落的知识点串起来。讨论范围以“前端页面开发 移动端”为主线覆盖路由、状态管理、数据请求、UI 组件、样式、表单和框架层尽量做到“每条线都有判断每个判断都有场景”。2. React 核心库你真正离不开的那几个2.1 React 本体与渲染器先区分一个容易混淆的概念react和react-dom是两个不同的包。react负责定义组件、维护虚拟 DOM、管理组件生命周期和 Hooksreact-dom则负责把 React 组件渲染到浏览器 DOM 上。没有react-dom页面不会出现在浏览器里。后端同学可以把 React 理解为“组件运行时”把react-dom理解为“浏览器适配层”。这两者分离带来了一个关键收益React 可以不止渲染到浏览器。react-native使用同一套组件模型渲染原生 iOS/Android 界面react-three-fiber可以接 WebGLreact-pdf可以直接渲染 PDF。这个“学习一次多处渲染”的架构是 React 生态能铺开这么多维度的根本原因。在 React 18 之后还引入了createRoot并发渲染模式支持自动批处理、transition、Suspense 数据加载等能力。React 19 又进一步收敛了 ref 转发、Context 用法等 API。对于普通业务开发者这些新特性大多数时候是“后台自动生效”的不需要每天感知到版本变化但要知道两个大版本的划分React 18 是并发渲染的起点React 19 是这些能力的稳定收敛期。具体选型时请以项目实际安装版本为准本文演示的 API 都是跨版本稳定的常用写法。2.2 React Router几乎所有项目的标配React 官方不提供路由社区的事实标准是react-router-dom最新包名也直接叫react-router。它解决的问题很朴素当 URL 变化时页面内容如何跟着变化并且不刷新整页。在版本 6 之前React Router 的核心用法是BrowserRouter包裹Routes和Route靠组件树完成路由匹配。v6.4 之后设计思路明显偏向了“数据路由”引入了createBrowserRouter和RouterProvider路由配置从 JSX 变成了一个独立的配置对象。这样做的最大好处是loader和action可以把数据加载和提交逻辑挂到路由上让 React Router 不止管 URL还能管一部分数据流。// 文件路径src/router.jsx import { createBrowserRouter, RouterProvider, Link } from react-router-dom; const router createBrowserRouter([ { path: /, element: Layout /, children: [ { index: true, element: Home / }, { path: users, element: Users /, loader: () fetch(/api/users).then((res) res.json()), }, ], }, ]); function Layout() { return ( div nav Link to/首页/Link Link to/users用户列表/Link /nav /div ); } export default function App() { return RouterProvider router{router} /; }这段代码里需要注意的点有三个index: true表示该路由是父路径/下的默认子路由访问/时渲染Home。loader在路由匹配到users时执行返回的数据可以通过useLoaderData()在组件里读取。RouterProvider替代了旧版里手动包裹的Routes整个应用只需要一个路由实例。如果你的项目是纯前端 SPAReact Router 是最稳妥的选择。如果项目是 Next.js 这类全栈框架不要手动安装 React Router框架自带基于文件系统的路由方案两套路由叠着用会冲突。3. 状态管理库从 Redux 一统到多方混战状态管理是 React 生态里讨论最多、最容易让新手焦虑的领域。先明确一个核心判断不是所有数据都需要全局状态。组件内部的输入框值、下拉选项用useState就足够只有跨组件共享、需要被多个不相关组件同时读写的数据才应该进入全局状态库。3.1 Redux Toolkit官方路线仍然稳定Redux 是 React 生态里历史最久的状态管理方案。它基于单向数据流和不可变更新的思想组件通过dispatch(action)通知reducerreducer返回一个新的 state组件再通过useSelector订阅变化。几乎所有新手都觉得 Redux 模板代码多。这个观点在旧版 Redux 上是成立的但 Redux Toolkit 已经极大改善了这一点。它把action creator、reducer、状态初始化全部收敛到一个createSlice里配合configureStore完成全局 store 的装配模板量已经接近“必要的开销”而不是“重复劳动”。// 文件路径src/store/userSlice.js import { createSlice, configureStore } from reduxjs/toolkit; const userSlice createSlice({ name: user, initialState: { name: , token: }, reducers: { setUser(state, action) { state.name action.payload.name; state.token action.payload.token; }, logout(state) { state.name ; state.token ; }, }, }); export const { setUser, logout } userSlice.actions; export const store configureStore({ reducer: { user: userSlice.reducer, }, });// 文件路径src/components/UserPanel.jsx import { useSelector, useDispatch } from react-redux; import { setUser, logout } from ../store/userSlice; function UserPanel() { const user useSelector((state) state.user); const dispatch useDispatch(); if (!user.name) { return button onClick{() dispatch(setUser({ name: Tom, token: abc }))}登录/button; } return ( div span当前用户{user.name}/span button onClick{() dispatch(logout())}退出/button /div ); }这里有一个细节值得注意createSlice底层使用 Immer所以你可以在 reducer 里直接修改state.name看似“可变”实际内部会生成不可变的新对象。这个设计大大降低了手写展开运算符做不可变更新的负担。Redux 适合的场景是复杂中后台项目多角色权限、复杂表单回显、多页面共享同一份用户登录状态、团队人数多且需要强约束协作。它的优点是约束清晰、DevTools 时间旅行调试强大、社区资料多缺点是概念偏多小项目里有点“杀鸡用牛刀”。3.2 Zustand轻量派代表如果 Redux Toolkit 还是觉得重Zustand 是当前轻量状态管理的事实标准。它的核心设计是“一个create函数 组件内通过 selector 订阅”几乎不需要配置 Provider也没有 action/reducer 的概念状态更新函数直接定义在 store 里。// 文件路径src/store/useUserStore.js import { create } from zustand; const useUserStore create((set) ({ user: null, login: (user) set({ user }), logout: () set({ user: null }), })); // 文件路径src/components/UserPanel.jsx function UserPanel() { const user useUserStore((s) s.user); const login useUserStore((s) s.login); return ( button onClick{() login({ name: Tom })} {user ? 用户${user.name} : 未登录} /button ); }Zustand 最值得学习的地方是它的“细粒度订阅”思路。组件通过 selector(s) s.user只订阅 store 里的user字段而不是整个 store这样某个字段更新时只有订阅它的组件会重新渲染。这与 Redux 的useSelector逻辑是一致的但写出来更少、更直接。适用场景非常清晰中小型项目、快速迭代的产品、H5 活动页、以及那些“不想引入一堆概念但确实需要全局状态”的团队。3.3 Jotai 与 MobX补充两个不同心智模型Jotai 是“原子状态”方案。它的思路是把状态拆成一个一个 atom组件通过useAtom组合使用。听起来抽象实际用起来很像“可以跨组件的 useState”。适合状态拆得很散、联动关系灵活的场景。MobX 则采用响应式编程模型核心是observableaction你直接修改被监听的对象页面会自动更新。它比 Redux 更“灵活”但也更需要团队自觉维护数据变更的边界否则状态流会变得不可控。// Jotai 示例 import { atom, useAtom } from jotai; const countAtom atom(0); function Counter() { const [count, setCount] useAtom(countAtom); return button onClick{() setCount((c) c 1)}{count}/button; }四种方案的心智模型可以这样对比方案心智模型模板代码量适合场景团队要求Redux Toolkit单向数据流 reducer中中大型中后台、多人协作高Zustand扁平 store selector低中小型项目、快速迭代低Jotai原子状态 组件组合低状态分散、联动灵活的页面低MobX响应式 自动追踪低数据模型复杂、类对象业务高3.4 状态管理怎么选给一个可以落地的选型建议如果你第一次做 React 项目没有历史包袱优先从 Zustand 开始。它简单、够用、踩坑成本低。如果项目是大型中后台且团队已经把 Redux 作为标准那么 Redux Toolkit 仍然是更稳的选择面试时也会高频考察。判断标准不是“哪个更好”而是“团队能否在半年后仍然理解这份代码”。另外要特别提醒不要把路由参数、接口返回的列表数据全部塞进全局状态。URL 参数应该从路由读取服务端数据应该交给数据请求库全局状态只保留“真正的客户端全局数据”比如登录用户、主题、国际化语言、权限标记。4. 数据请求与服务端状态React Query 和 SWR 的崛起4.1 传统写法的痛点很多新手写数据请求第一反应是在useEffect里写fetchuseEffect(() { fetch(/api/users) .then((res) res.json()) .then(setList) .catch(setError); }, []);这个写法在小示例里没问题但放到真实项目里会逐渐积累出四类问题loading/error 状态要手动维护每写一个接口都要重复一遍同样的代码。切换页面或重复点击时请求可能重复发出没有缓存也没有去重。竞态问题上一次请求还没有返回下一次请求已经发出先发的请求后返回页面数据被旧数据覆盖。修改数据后无法自动刷新需要手动重新调用接口很容易遗漏。这些问题本质上是因为服务端数据不是“组件内的本地状态”它有自己的生命周期——需要缓存、需要失效、需要后台刷新。把服务端状态和客户端状态混在一起才会越写越乱。4.2 TanStack QueryReact QueryReact Query 是目前最主流的数据请求库新版包名为tanstack/react-query。它把服务端状态模型化为queryKeyqueryFn并内置缓存、重试、失效、后台刷新、分页、无限滚动等能力。// 文件路径src/hooks/useUsers.js import { useQuery } from tanstack/react-query; async function fetchUsers() { const res await fetch(/api/users); if (!res.ok) throw new Error(请求失败); return res.json(); } export function useUsers() { return useQuery({ queryKey: [users], queryFn: fetchUsers, staleTime: 30 * 1000, }); }// 文件路径src/components/UserList.jsx import { useUsers } from ../hooks/useUsers; function UserList() { const { data, isLoading, error } useUsers(); if (isLoading) return p加载中.../p; if (error) return p加载失败{error.message}/p; return ( ul {data.map((user) ( li key{user.id}{user.name}/li ))} /ul ); }这里的核心概念是queryKey。它不只是缓存键还是“同一份数据的身份标识”。当你在另一个组件里调用useQuery({ queryKey: [users] })只要[users]相同React Query 会自动复用缓存。当用户提交表单、修改数据后通过queryClient.invalidateQueries({ queryKey: [users] })使缓存失效数据会自动重新拉取。这种“以 key 为中心”的写法比在useEffect里手动维护数据状态要可靠得多。4.3 SWR另一个热门选择SWR 是 Next.js 团队开源的数据请求库名字来自 HTTP 缓存策略stale-while-revalidate。核心思路是先返回缓存里的旧数据同时在后台重新验证验证通过后更新到最新数据。API 设计和 React Query 高度相似useSWR(/api/users, fetchUsers)一行就能完成数据请求。SWR 与 React Query 的差异更多在细节React Query 的功能更全面缓存管理和 devtools 更成熟SWR 更加轻量依赖更少与 Next.js 集成顺畅。两者选哪个都不会犯大错关键是团队统一不要一个项目里同时混用两个请求库。4.4 与 axios 的分工很多同学会把axios和 React Query 对立起来其实它们是配合关系。axios 是 HTTP 客户端负责网络层设置请求头、处理超时、拦截响应、统一错误提示。React Query 负责数据状态层缓存、失效、重试、并发去重。正确的组合方式是用 axios 封装好request函数在 React Query 的queryFn里调用它。import axios from axios; const http axios.create({ baseURL: /api, timeout: 10000, }); http.interceptors.response.use( (response) response.data, (error) Promise.reject(error) ); export default http;这样网络逻辑和状态逻辑各司其职业务代码不需要关心请求细节也不需要手写状态管理。5. UI 组件库Ant Design、Material UI 与无头组件5.1 面向中后台的 Ant Design在中国技术社区React 项目的 UI 组件库首选几乎默认是 Ant Design。原因很实际它组件类型极其丰富Table、Form、Modal、Select、DatePicker等中后台高频组件开箱即用而且默认样式和交互已经过大量业务验证团队不用自己设计复杂表格、权限弹窗和表单校验。Ant Design 的典型用法是一整套体系antd是 React 组件库ant-design/icons是图标库新版也提供基于 CSS-in-JS 的主题定制能力。中后台项目通常还会配合 Pro Components 这类更上层的封装直接生成带查询表单、编辑弹窗、详情页的完整页面骨架。5.2 面向 C 端体验的 Material UIMaterial UI 是 Google Material Design 的 React 实现风格以动效、圆角、阴影和明确的交互反馈见长非常适合 C 端产品、富交互应用、后台仪表盘这类对视觉要求较高的场景。它的主题系统很强大可以通过ThemeProvider统一管理颜色、间距、字体但自定义不同组件风格时需要一定的学习成本。5.3 无头组件只提供逻辑不提供样式无头组件Headless Components是近几年的新趋势代表是 Radix UI、Headless UI。这类库把交互逻辑键盘导航、焦点管理、无障碍属性、弹窗开关封装好但完全不提供 CSS样式完全由开发者控制。如果你当前项目使用 Tailwind CSS 这类原子化样式方案无头组件是很好的搭配既有良好的可访问性交互又不会被组件库的默认样式束缚。代价是开发成本更高需要自己组装样式和布局。UI 库的选择没有绝对的“最优”更靠谱的判断方式是看业务形态内部管理系统、运营后台选 Ant Design开发效率最高。对外 C 端应用看设计风格Material Design 优先 Material UI否则可以 Tailwind 无头组件。组件库风格需要高度定制优先考虑无头组件不要和大型组件库的默认样式对抗。6. 样式方案CSS Modules、CSS-in-JS 与原子化 CSSReact 组件化之后样式方案也经历了几波迭代。目前主流的有三条路线各自有明确的适用场景。6.1 CSS Modules最稳定的传统方案CSS Modules 让每个 CSS 文件默认拥有局部作用域通过构建工具把你的类是btn编译成_btn_hash123组件内import styles from ./Button.module.css之后用styles.btn引用。它兼顾了普通 CSS 的熟悉感又解决了全局命名冲突问题。它是当前工程化最成熟、构建链路最稳定的方案适合对样式方案没有特殊需求的大部分业务。6.2 CSS-in-JS适合动态主题和组件库CSS-in-JS 的代表是styled-components它把 CSS 写进 JS// 文件路径src/components/Button.jsx import styled from styled-components; const StyledButton styled.button background: ${(props) (props.primary ? #1677ff : #fff)}; color: ${(props) (props.primary ? #fff : #1677ff)}; border: 1px solid #1677ff; padding: 8px 16px; border-radius: 4px; ; function Button(props) { return StyledButton {...props} /; }它的最大优势是“状态与样式天然联动”。按钮的primary、danger、disabled状态直接通过 props 控制不用另外写一堆条件类名。同时主题切换暗色模式、品牌色可以通过 ThemeProvider 在运行时动态改变。代价是运行时开销——所有样式都变成了 JS 对象首次渲染和更新时性能比纯 CSS 略差。中后台系统、组件库、需要复杂主题定制的项目适合这种方案。6.3 原子化 CSSTailwind CSS 与 UnoCSSTailwind CSS 属于原子化 CSS核心思路是“用工具类替代自定义 CSS”。你不需要起类名、不需要写margin-bottom: 16px直接写mb-4就完成了一个内边距样式。function Card({ title, children }) { return ( div classNamerounded-lg border border-gray-200 p-6 shadow-sm h3 classNametext-lg font-semibold text-gray-800{title}/h3 div classNamemt-2 text-gray-600{children}/div /div ); }Tailwind 的优点类名组合稳定、样式约束统一、开发时不用频繁切换 CSS 文件、配合编辑器插件效率很高。缺点JSX 的类名会变得很长维护时对团队的类名规范要求高。UnoCSS 是 Tailwind 的轻量级替代启动和热更新更快许多新的 Vite 项目会选用。三条路线的选择建议方案启动成本样式复用运行时开销动态主题推荐场景CSS Modules低中无弱常规业务系统CSS-in-JS中高有强组件库、主题定制Tailwind / UnoCSS中高低中C 端快速迭代项目需要提醒的是不要在一个新项目里同时混用 Tailwind 和 CSS Modules两种思维模型会互相干扰导致同样的页面里一部分是工具类、一部分是局部类名。选一条主线团队约定清楚比追求“用满所有方案”重要得多。7. 表单与校验React Hook Form Zod表单是中后台项目里最容易写又最容易被轻视的部分。手写表单会频繁遇到这几个问题输入框数量一多useState就得写很多个每次输入都触发整个页面重新渲染校验逻辑分散在提交事件和各个输入事件里改起来很痛苦。React Hook Form 的思路是用非受控组件 ref 来管理表单。它不把每个输入框的值实时存进 state而是通过注册register函数让表单自己管理输入 DOM。这样一来输入时不会导致页面级 re-render性能比纯受控表单好很多。配合zodResolver可以使用 Zod 的 schema 来统一定义校验规则。// 文件路径src/components/LoginForm.jsx import { useForm } from react-hook-form; import { zodResolver } from hookform/resolvers/zod; import { z } from zod; const loginSchema z.object({ username: z.string().min(3, 用户名至少 3 个字符), password: z.string().min(6, 密码至少 6 位), }); function LoginForm() { const { register, handleSubmit, formState: { errors, isSubmitting }, } useForm({ resolver: zodResolver(loginSchema) }); const onSubmit async (data) { // 在这里调用登录接口 console.log(提交的数据, data); }; return ( form onSubmit{handleSubmit(onSubmit)} div input placeholder用户名 {...register(username)} / {errors.username p{errors.username.message}/p} /div div input typepassword placeholder密码 {...register(password)} / {errors.password p{errors.password.message}/p} /div button typesubmit disabled{isSubmitting} {isSubmitting ? 提交中... : 登录} /button /form ); }这段代码里的关键设计是注册的字段名写在register(username)里React Hook Form 会自动把输入值绑定到data对象的username字段上。Zod 的 schema 同时承担校验类型和错误文案两层责任比手写if (value.length 3)要简洁得多。在 Ant Design 生态中很多人也会直接用 antd 自带的Form组件它本身已经集成了校验能力。如果你的项目已经深度使用 antd且表单复杂度要求不高用 antdForm就够了如果需要高度定制表单交互或表单结构复杂、动态增删字段多React Hook Form Zod 是更现代的选型。8. 框架层Next.js 与 React Native8.1 Next.js从 SPA 走向全栈当 React 应用需要 SEO、首屏性能、服务端渲染时纯 Vite React 的方案就不够用了。Next.js 是目前最主流的 React 全栈框架它解决了两个核心问题一套代码同时支持 SSR/SSG/ISR/CSR 多种渲染模式以及通过文件系统路由约定替代手动配置路由。Next.js 的 App Router 模式是当前主线方向。目录结构以app文件夹为中心每个文件夹对应一个路由page.jsx定义该路由的页面layout.jsx定义布局loading.jsx定义加载态// 文件路径app/layout.jsx export default function RootLayout({ children }) { return ( html langzh-CN body{children}/body /html ); }// 文件路径app/users/page.jsx async function getUsers() { const res await fetch(https://api.example.com/users, { cache: no-store }); return res.json(); } export default async function UsersPage() { const users await getUsers(); return ( ul {users.map((user) ( li key{user.id}{user.name}/li ))} /ul ); }注意 App Router 的page组件默认是服务端组件可以直接await请求数据然后把结果渲染到页面。这个写法相比传统 SPA 的 useEffect loading 状态代码量大幅减少。但也要注意服务端组件里不能直接使用只在浏览器端生效的 Hooks比如useState如果需要在客户端写交互逻辑需要在文件顶部加use client指令。一个小项目的初始模板可以是 Vite React React Router Zustand React Query Tailwind React Hook Form浏览器管理端/内部工具如果是需要 SEO 或全栈能力的内容网站、电商页面则直接选 Next.js。8.2 React Native同一套心智另一套平台React Native 让 React 开发者可以编写原生移动应用。它复用 React 的组件模型但不用 DOM而是渲染成原生视图。运行时启动白屏、原生模块依赖、Android 和 iOS 适配这些问题是这个领域的常见挑战。如果团队只有前端背景且目标是跨端快速上线也可以考虑 Expo 生态来简化打包和调试流程。8.3 从更广视野看“React 相关词”在搜索 React 相关资料时还会看到 ReActReasoning and Acting一个大语言模型 Agent 的模式这个词。它和前端 React 不是同一个东西。ReAct 研究的是“推理 行动交替进行”的智能体模式React 是 Meta 开源的 UI 库。如果面试被人问起要能区分这两者避免概念混淆。9. 常见误区与排查思路9.1 新手最容易踩的坑问题现象可能原因排查方式解决方案页面渲染后一直重复请求接口useEffect没有写依赖数组或依赖数组里放了对象/函数查看网络请求日志确认请求是否持续发出补全依赖数组用useCallback稳定函数引用修改 store 后页面不更新直接修改了 state 对象没有生成新引用在 Redux DevTools 里查看状态变更记录使用 reducer 或set方法更新不可变状态路由跳转后页面组件不重新加载数据组件复用了旧的列表数据没有清理上次数据检查queryKey是否与页面参数一致在 queryKey 中加入路由参数表单输入很卡、每次敲一个字都掉帧使用受控组件且全局状态频繁更新打开 Performance 面板查看渲染耗时改用 React Hook Form 或拆分状态粒度同一个接口在多个页面重复请求没有使用数据请求库的缓存直接在组件内 fetch查看 Network 中是否存在大量重复请求使用 TanStack Query 或 SWR 统一管理服务端状态home 页面打包体积过大路由配置里所有页面都被打进一个 bundle查看构建产物各 chunk 大小对非首屏路由做React.lazy代码分割9.2 选型时的常见误判第一个误区是“React 项目必须用 Redux”。现在的小项目完全可以用 Zustand甚至只靠 Context 也能撑住简单的主题、登录信息共享。Redux 不是不用不行而是要看规模。第二个误区是“框架越全越好”。Next.js 功能强大但会同时引入 Node.js 服务端、文件路由和 RSC 等概念。如果你的项目只是纯前端部署在 CDN 后台Vite React 反而是更轻的选择。第三个误区是“组件库能解决所有 UI 问题”。组件库只能解决通用组件业务里的复杂布局、特殊交互仍然需要自己写。选择组件库时要确认它支持你接下来要做的关键功能比如动态表格、可编辑单元格、复杂树形结构。10. 总结与后续学习方向这篇文章围绕 React 生态梳理了“路由、状态、数据请求、UI、样式、表单、框架”这些线的代表库。真正值得记住的内容可以压缩成三句话React 本体只负责 UI工程能力完全靠生态组合。服务端状态和客户端状态要分开管理React Query / SWR 属于服务端状态Redux / Zustand / Jotai 属于客户端状态。选型不是收集“最流行”库而是围绕业务形态挑一组能长期维护的组合。从实践上看建议你按这个顺序推进第一步用 Vite 手动搭建一个 React 项目只加 React Router体验一次完整的 SPA 页面切换。第二步在项目里引入 Zustand做一个跨组件的购物车或用户登录信息模块理解全局状态的作用边界。第三步接入 TanStack Query把列表页的数据请求从 useEffect 迁移到 useQuery感受缓存、loading 和失效刷新带来的体验变化。第四步用 React Hook Form Zod 重写一个表单页把校验逻辑收敛到 schema 里。第五步再做一次技术选型决策题“如果这个项目需要被搜索引擎收录我应该选什么技术栈”由此开始了解 Next.js 的 App Router 和服务端组件。React 生态确实庞杂但它的每一条分支都在解决一类明确的工程问题。学 React 的过程本质上是学会判断“这个问题属于哪一层应该用哪个库去解决”。理解了这个分层再看社区出来的任何新库都能快速定位它在整个体系中的位置。建议把这篇文章收藏备用也可以根据你的实际项目情况写一份团队内部的技术选型清单把每个库的使用边界定清楚。
返回列表