Back

状态管理:内置工具 vs 外部库

状态管理:内置工具 vs 外部库

每个前端开发者在应用程序开始增长时都会面临同样的问题:我应该坚持使用框架的内置状态管理工具,还是选择外部库?答案并不像许多教程所说的那么直接。

本文探讨了使用内置状态管理功能(如 React 的 hooks、Angular 的 services 或 Vue 的响应式系统)与采用外部库(如 Redux、Zustand 或 Pinia)之间的权衡。我们将专注于可能需要扩展的中小型项目的实际决策制定。

核心要点

  • 内置状态管理工具提供简洁性和零依赖,但在复杂状态共享方面可能遇到困难
  • 外部库提供强大功能,如时间旅行调试和中间件,但增加了复杂性和包体积
  • 混合方法通常效果最佳,对简单状态使用内置工具,对特定复杂功能使用库
  • 基于实际需求而非预期问题进行选择——从简单开始,当感受到真正痛点时再迁移

理解状态管理基础

状态管理是应用程序如何随时间跟踪和更新数据。每个框架都提供基本工具来处理这个问题:

  • 组件状态:存在于单个组件内的本地数据
  • 共享状态:被多个组件访问的数据
  • 全局状态:应用程序范围的数据,如用户认证或主题偏好

内置工具处理这些场景的方式与外部库不同,理解这些差异对于做出正确选择至关重要。

内置状态管理:简洁优先

现代框架开箱即用地提供了强大的状态管理能力。React 提供了 useStateuseContext 等 hooks,Angular 有 services 和 RxJS,而 Vue 提供了响应式系统和 provide/inject 模式。

内置工具的优势

零额外依赖:保持包体积精简。没有额外库意味着更快的加载时间和更简单的依赖管理。

框架对齐:内置工具被设计为与其他框架功能无缝协作。它们遵循团队已经熟悉的相同模式和约定。

最小学习曲线:新开发者可以立即贡献代码,无需学习额外的状态管理模式。

属性钻取问题

当需要在距离较远的组件之间共享状态时,主要限制就会出现。你最终会通过多个组件层传递 props——这种模式被称为”属性钻取”:

// 状态必须通过 Parent 传递,即使它并不使用这个状态
function Grandparent() {
  const [user, setUser] = useState(null);
  return <Parent user={user} />;
}

function Parent({ user }) {
  return <Child user={user} />;
}

function Child({ user }) {
  return <div>Hello {user?.name}</div>;
}

外部库:强大功能与结构

ReduxZustandMobXPinia 这样的状态管理库为复杂状态逻辑提供了集中式存储和成熟的模式。

外部库的优势场景

复杂状态逻辑:具有复杂数据关系的应用程序受益于集中式存储。比如文档编辑器、仪表板或实时协作工具。

时间旅行调试:像 Redux 这样的库提供强大的开发者工具来跟踪状态随时间的变化——对调试复杂交互非常宝贵。

中间件和插件:需要撤销/重做功能?本地存储持久化?许多库通过简单的中间件提供这些功能。

隐性成本

增加的复杂性:每个外部库都引入新概念。你的团队必须学习 actions、reducers、selectors 或库使用的任何模式。

样板代码问题:虽然像 Redux Toolkit 这样的现代工具已经显著减少了样板代码,但你仍然要比内置解决方案编写更多代码。

性能开销:外部库增加了包体积。虽然 Zustand 只增加 8KB,Redux Toolkit 增加约 40KB——虽然不算巨大,但会累积。

做出正确选择:实用框架

基于实际经验的决策框架:

在以下情况下从内置工具开始:

  • 构建 CRUD 应用程序或内容驱动的网站
  • 与小团队合作或时间紧迫
  • 你的状态主要由表单数据和 API 响应组成
  • 组件相对独立

在以下情况下考虑外部库:

  • 多个组件需要修改相同的复杂状态
  • 你需要撤销/重做、状态持久化或乐观更新等功能
  • 你的应用程序具有实时协作功能
  • 状态逻辑变得难以测试或理解

中间地带:混合方法

你不需要采用全有或全无的方法。许多成功的应用程序使用:

  • 内置状态用于组件特定的 UI 状态
  • Context 或 provide/inject 用于主题和认证
  • 像 Zustand 这样的轻量级库用于复杂功能
  • React QuerySWR 这样的服务器状态库用于 API 数据

这种混合方法在解决特定痛点的同时保持较低的复杂性。

性能考虑

包体积很重要,但这不是全部。考虑:

运行时性能:在 React 中,内置的 context 可能导致不必要的重新渲染。外部库通常通过订阅机制更好地优化这一点。

开发者性能:如果你的团队花费数小时调试状态问题,Redux Toolkit 的 40KB 可能是值得的。

维护性能:结构良好的状态管理减少了错误,使功能更容易添加。

避免常见陷阱

过早过度工程:不要为待办事项应用添加 Redux。从简单开始,当感受到真正痛点时再迁移。

全局存储一切:不是所有状态都属于全局存储。尽可能保持组件状态本地化。

忽略服务器状态:API 数据与 UI 状态有不同的需求。考虑使用专门的数据获取和缓存工具。

结论

内置和外部状态管理之间的选择不是关于哪个”更好”——而是关于将工具与你的特定需求匹配。从框架提供的工具开始。当你发现自己在与框架限制作斗争时,那就是外部库变得有价值的时候。

对于大多数中小型项目,内置工具结合良好的组件架构就足够了。但当复杂性增长时,不要犹豫采用外部库——只要确保你在解决真正的问题,而不是想象中的问题。

常见问题

是的,混合方法很常见且经常被推荐。对简单的组件 UI 状态使用内置状态,对认证或主题使用 context,对需要集中管理的复杂功能使用像 Zustand 或 Redux 这样的外部库。

当你通过三个或更多组件层传递 props,而中间组件不使用这些数据时,属性钻取就成了问题。如果重构变得困难或你在重复状态逻辑,就该考虑替代方案了。

当 context 值的任何部分发生变化时,React Context 可能导致不必要的重新渲染,影响所有消费组件。对于频繁更新的状态,像 Zustand 或 Redux 这样基于订阅更新的外部库通常性能更好。

Redux 对于需要可预测状态更新和出色调试工具的大型应用程序仍然很有价值。然而,对于大多数项目,像 Zustand 或 Jotai 这样的轻量级替代方案以更少的样板代码提供了类似的好处。

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay