The History of Redux">The History of Redux">
跳至主要内容

Redux 的简史

2011: JS MVC 框架

早期的 JavaScript MVC 框架,如 AngularJS、Ember 和 Backbone,存在一些问题。AngularJS 试图强制执行“控制器”与模板的分离,但无法阻止你在模板中编写 <div onClick="$ctrl.some.deeply.nested.field = 123">。同时,Backbone 基于事件发射器 - 模型、集合和视图都能够发射事件。模型可能会发射一个 "change:firstName" 事件,视图会订阅这些事件。但是,任何代码都可以订阅这些事件并运行更多逻辑,这可能会触发更多事件

这使得这些框架非常难以调试和维护。更新一个模型中的一个字段可能会触发数十个事件和应用程序周围运行的逻辑,或者任何模板都可以在任何时候更改状态,这使得无法理解更新状态时会发生什么。

2014: Flux

大约在 2012-2013 年,当 React 首次公开发布时,Facebook 在内部使用它已经有一两年了。他们遇到的一个问题是,他们有多个独立的 UI 部分需要访问相同的数据,例如“有多少未读通知”,但他们发现使用 Backbone 风格的代码很难保持逻辑清晰。

Facebook 最终提出了一个名为“Flux”的模式:创建多个单例 Store,例如 `PostsStore` 和 `CommentsStore`。每个 Store 实例都会注册到一个 `Dispatcher` 上,而触发 Store 更新的唯一方式是调用 `Dispatcher.dispatch({type: "somethingHappened"})`。这个简单的对象被称为“action”。其理念是将所有状态更新逻辑半集中化——你不能让应用程序的任何随机部分随意修改状态,并且所有状态更新都应该是可预测的。

Facebook 大约在 2014 年宣布了这个“Flux 架构”的概念,但没有提供一个完全实现该模式的库。这导致 React 社区构建了数十个受 Flux 启发的库,这些库对该模式进行了各种变体。

2015 年:Redux 的诞生

在 2015 年年中,Dan Abramov 开始构建另一个受 Flux 启发的库,名为 Redux。其理念是为一个会议演讲演示“时间旅行调试”。该库旨在使用 Flux 模式,但应用了一些函数式编程原则。与其使用 Store 实例,不如使用可预测的 reducer 函数进行不可变更新。这将允许在时间线上跳来跳去,查看状态在不同时间点的状态。它还将使代码更直接、更易于测试和理解。

Redux 于 2015 年发布,并迅速淘汰了所有其他受 Flux 启发的库。它在 React 生态系统中获得了高级开发人员的早期采用,到 2016 年,许多人开始说“如果你使用 React,你一定也要使用 Redux”。(坦率地说,这导致许多人在他们不需要使用 Redux 的地方使用它!)

还值得注意的是,当时 React 只有它的遗留 Context API,它基本上是坏掉的:它无法正确地向下传递更新的值。因此,可以将事件发射器放入 Context 并订阅它们,但你无法真正将其用于普通数据。这意味着许多人开始采用 Redux,因为它一种在整个应用程序中一致地传递更新值的方法。

丹很早就说过,“Redux 的目标不是写出最短的代码,而是让代码可预测且易于理解”。这其中一部分是关于拥有一个一致的模式(状态更新由 reducer 完成,因此你总是查看 reducer 逻辑来了解状态值 *可以* 是什么,可能的动作是什么,以及它们会导致什么更新)。它也是关于将逻辑 *移出* 组件树,以便 UI 主要只是说“发生了这件事”,而你的组件更简单。除此之外,像 reducer 和 selector 这样的“纯函数”编写的代码更容易理解:输入参数,输出结果,没有其他需要查看的内容。最后,Redux 的设计使 Redux DevTools 成为可能,它向你展示了所有已分派的 action 的可读列表,包括 action/state 中包含的内容,以及每个 action 发生的更改。

早期的 Redux 模式特别依赖于样板代码。通常会有 actions/todos.jsreducers/todos.jsconstants/todos.js,仅仅是为了定义一个单一的 action 类型(const ADD_TODO = "ADD_TODO")、action 创建器函数和 reducer case。你还需要使用扩展运算符手动编写不可变更新,这很容易出错。人们确实在 Redux 中获取和缓存服务器状态,但需要大量的代码手动编写 thunk 来执行获取操作,使用获取到的数据分派 action,并在 reducer 中管理缓存状态。

Redux 尽管有这些样板代码,但仍然很受欢迎,但它一直是最令人担忧的地方。

2017 年:生态系统竞争

到 2017-18 年,情况发生了变化。社区中许多人现在更关注“数据获取和缓存”,而不是“客户端状态管理”,而正是那时我们看到了像 Apollo、React Query、SWR 和 Urql 这样的数据获取库的兴起。与此同时,我们还有 *新的* React Context API 发布,它可以正确地将更新后的值传递到组件树中。

这意味着 Redux 并不像以前那样“必不可少”了——现在有其他工具可以解决许多相同的问题,并且有不同程度的重叠(而且通常代码更少)。关于“样板代码”的频繁抱怨也让许多使用 Redux 的人感到担忧。

2019 年:Redux Toolkit

因此,在 2019 年,我们构建并发布了 Redux Toolkit,它提供了一种更简单的方法来编写相同的 Redux 逻辑,但代码更少。RTK 仍然是“Redux”(单一存储,分派操作以触发状态更新,这些更新通过不可变更新逻辑在 reducer 中完成),但它具有更简单的 API 和更好的内置默认行为。这还包括 RTK Query,我们受 React Query 和 Apollo 启发的内置数据获取和缓存库。

如今,RTK 是编写 Redux 逻辑的标准方法。像所有工具一样,它也存在权衡。RTK 可能比 Zustand 需要更多代码,但它也为将应用程序逻辑与 UI 分离提供了有用的模式。Redux 并非适合所有应用程序的工具,但它仍然是 React 应用程序中最广泛使用的状态管理库,拥有出色的文档,并提供许多功能来帮助您构建具有始终如一且可预测结构的应用程序。

更多信息