什么是 Redux:设计师指南

已发表: 2022-03-10
快速总结↬你知道 Redux 的真正威力不只是管理状态吗? 您想在设计时考虑到 Redux 的工作原理吗? 让我们更深入地了解 Redux 可以做什么,为什么要做它的事情,有什么缺点,以及它与设计的关系。

你听说过 Redux 吗? 它是什么? 请不要谷歌搜索!

  • “花哨的后端东西。”
  • “我听说过,但我不知道它是什么。 也许它是一个 React 框架?”
  • “在 React 应用程序中存储和管理状态的更好方法。”

我已经向 40 多名设计师提出了这个问题。 以上是他们的典型回答。 他们中的许多人都知道 Redux 与 React 一起工作,它的工作是“状态管理”。

但是你知道这个“状态管理”的真正含义吗? 你知道 Redux 的真正力量不只是管理状态吗? 你知道Redux 不一定需要 React 才能工作吗? 你想加入你的团队关于是否使用 Redux 的讨论(或至少午餐聊天)吗? 您想在设计时考虑到 Redux 的工作原理吗?

在本文的帮助下,我想向您展示 Redux 的全貌:它可以做什么,为什么要做它,有什么缺点,什么时候使用它,以及它与设计的关系。

我的目标是帮助像你这样的设计师。 即使您之前没有编写过任何代码,我认为理解 Redux 仍然是可能的并且有益(并且有趣)的。 期待简单的英语和涂鸦——没有代码或抽象的谈话。

准备好骑行了吗?

跳跃后更多! 继续往下看↓

什么是 Redux?

在超高层次上,Redux 是开发人员用来让他们的生活更轻松的工具。 你们中的许多人可能已经听说过,它的工作是“状态管理”。 稍后我将解释状态管理的含义。 在这一点上,我会给你留下这张照片:

Redux 状态管理器。
Redux 管理状态,但在后台,有一些隐藏的权力。 (Beebee 的插图)(大预览)

你为什么要关心?

Redux 更多的是关于应用程序的内部运作,而不是它的外观。 这是一个有点复杂的工具,学习曲线陡峭。 这是否意味着我们作为设计师应该远离它?

不,我认为我们应该接受它。 汽车设计师应该了解发动机的用途,对吧? 为了成功设计应用程序界面,设计师还应该对底层事物有扎实的了解。 我们应该了解它可以做什么,了解开发人员使用它的原因,并了解它的优点和含义。

“设计不仅仅是它的外观和感觉。 设计就是它的运作方式。”

——史蒂夫·乔布斯

Redux 能做什么?

许多人使用 Redux 来管理 React 应用程序中的状态。 这是最常见的用例,Redux 改进了 React 做得不好的方面(还)。

但是,您很快就会发现 Redux 的真正威力远不止于此。 让我们从了解状态管理的真正含义开始。

状态管理

如果你不确定这个“状态”是什么意思,让我们用一个更通用的术语来代替它:“数据”。 状态是不时变化的数据。 状态决定了用户界面上显示的内容。

状态管理是什么意思? 一般来说,我们需要在应用程序中管理三个方面的数据:

  1. 获取和存储数据
  2. 将数据分配给 UI 元素
  3. 更改数据

假设我们正在构建一个 Dribbble 投篮页面。 我们要在页面上显示的数据是什么? 它们包括作者的个人资料照片、姓名、动画 GIF、心数、评论等。

Dribbble 投篮页面上的数据
Dribbble 投篮页面上的数据(大预览)

首先,我们需要从云中的服务器获取所有这些数据并将其放在某个地方。 接下来,我们需要实际显示数据。 我们需要将这些数据的片段分配给相应的 UI 元素,这些元素代表我们在浏览器中实际看到的内容。 例如,我们将头像的 URL 分配给 HTML img标签的src属性:

 <img src='https://url/to/profile_photo'>

最后,我们需要处理对数据的更改。 例如,如果用户向 Dribbble 投篮添加新评论或添加星标,我们需要相应地更新 HTML。

协调状态的这三个方面是前端开发的重要组成部分, React 对此任务有不同程度的支持。 有时,React 中的内置工具运行良好。 但是随着应用程序变得越来越复杂,它的状态可能会变得更难单独使用 React 来管理。 这就是为什么许多人开始使用 Redux 作为替代方案的原因。

获取和存储数据

在 React 中,我们将 UI 分解为组件。 这些组件中的每一个都可以分解为更小的组件(请参阅“什么是 React?”)。

Dribbble 拍摄页面分解为组件
Dribbble 拍摄页面分解为组件(大预览)

如果我们的 UI 是这样构造的,我们什么时候获取数据以及在填充 UI 之前将数据存储在哪里?

想象一下,每个组件中都有一个厨师。 从服务器获取数据就像采购准备菜肴所需的所有原料。

一种天真的方法是在需要的地方和时间获取和存储数据。 这就像每个厨师出去直接从遥远的农场购买蔬菜和肉类。

天真的方式:从每个组件中获取数据。
天真的方式:从每个组件中获取数据。 (Beebee 的插图)(大预览)

这种方法很浪费。 我们需要从许多组件多次调用服务器——即使是相同的数据。 厨师们来回走动会浪费大量的汽油和时间。

使用 Redux,我们获取一次数据并将其存储在一个中心位置,方便地称为“存储”。 然后,任何组件都可以随时使用数据。 这与附近有一家超市没什么不同,我们的厨师可以在那里购买所有食材。 超市派卡车从农场运回散装蔬菜和肉类。 这比要求个别厨师亲自去农场效率高得多!

这家商店也是唯一的事实来源。 组件总是从存储中检索数据,而不是从其他任何地方检索数据。 这使所有 UI 内容保持一致。

Redux 作为数据的中央存储。
Redux 作为数据的中央存储。 (Beebee 的插图)(大预览)

将数据分配给 UI 元素

只有 React,实际上有更好的方法来获取和存储数据。 我们可以请我们非常友善的厨师 Shotwell 为他所有的厨师朋友购物。 他会开卡车去农场,把好吃的东西带回去。 我们可以从容器组件中获取数据,例如 Dribbble 示例中的“Shot”组件,并将其用作唯一的事实来源。

从根组件获取数据。
从根组件获取数据。 (Beebee 的插图)(大预览)

这种方法比从每个组件获取数据的简单方法更有效。 但肖特韦尔是如何将食材传递给其他厨师的呢? 如何将数据传递给实际呈现 HTML 元素的组件? 我们将数据从外部组件传递到内部组件,就像中继中的指挥棒一样,一直到数据到达目的地。

例如,作者头像的 URL 需要从“Shot”传递到“ShotDetail”,再到“Title”,最后传递到<img>标签。 如果我们的厨师住在公寓里,它真的是这样的:

通过 props 将数据传递给组件。
通过 props 将数据传递给组件。 (Beebee 的插图)(大预览)

要将数据传送到目的地,我们必须让路径上的所有组件都参与进来,即使它们根本不需要数据。 如果有很多楼层真的很烦人!

如果超市送货上门怎么办? 使用 Redux 1 ,我们可以将任何数据插入到任何组件中,而完全不影响其他组件,如下所示:

1绝对准确地说,是另一个名为react-redux的库将数据交给 React 组件,而不是 Redux 本身。 但是由于 react-redux 只是做管道,人们几乎总是一起使用 Redux 和 react-redux,我认为将其作为 Redux 的好处之一包括在内是很好的。

使用 Redux 将数据插入组件。
使用 Redux 将数据插入组件。 (Beebee 的插图)(大预览)

注意在最新版本的 React (16.3) 中,有一个新的“上下文”API,它在将数据插入组件方面几乎完成了相同的工作。 因此,如果这是您的团队使用 Redux 的唯一原因,请认真考虑升级到 React 16.3! 查看官方文档以获取更多信息(警告:前面有很多代码)。

更改数据

有时,在应用程序中更新数据的逻辑可能相当复杂。 它可能涉及多个相互依赖的步骤。 在更新应用程序状态之前,我们可能需要等待来自多个服务器的响应。 我们可能需要在不同的时间、不同的条件下更新该州的许多地方。

如果我们没有一个好的结构来处理所有这些逻辑,这可能会让人不知所措。 代码将难以理解和维护。

Redux 允许我们分而治之。 它提供了一种将数据更新逻辑分解为小的“reducer”的标准方法。 这些减速器和谐地协同工作以完成一个复杂的动作。

将复杂的逻辑划分为 reducer。
将复杂的逻辑划分为 reducer。 (Beebee 的插图)(大预览)

不过,请密切关注 React 的最新发展。 与“上下文”API 一样,未来版本的 React 中可能会有一个新的“setState”API。 它可以更容易地将复杂的更新逻辑分解成更小的部分。 一旦这个新 API 可用,您可能不再需要 Redux 来管理状态管理的这方面。

Redux 的真正力量

到目前为止,Redux 似乎只是 React 的一个创可贴。 人们使用 Redux 来改进 React(目前)做得不好的方面。 但 React 正在迅速追赶! 事实上,Redux 的创建者 Dan Abramov 几年前就加入了 Facebook 的 React 核心团队。 他们一直在忙于对 React 进行上述改进:上下文 API(在 16.3 中发布)、更好的数据获取 API(在 2018 年 2 月演示)、更好的 setState API 等等。

它会让 Redux 过时吗?

你猜怎么了? 我还没有向你展示 Redux 的真正威力!

Redux 的强大功能远远超出了状态管理。
Redux 的强大功能远远超出了状态管理。 (Beebee 的插图)(大预览)

Redux 迫使开发人员遵循一些严格的规则,这给 Redux 带来了很多力量(是的,纪律的力量!):

  1. 所有数据(应用程序状态)都必须以明文形式描述。 您应该能够用笔在纸上写下所有数据。
  2. 每个动作(数据更改)都必须用明文描述。 在更改任何内容之前,您必须写下您将要做什么。 你不能在不留下标记的情况下更改数据。 这个过程在 Redux 俚语中称为“调度动作”。
  3. 您更改数据的代码必须表现得像数学公式。 给定相同的输入,它必须返回相同的结果。 无论您运行多少次,4 的平方始终为 16。

当您遵循这些规则来构建应用程序时,奇迹就会发生。 它实现了许多很酷的功能,否则这些功能很难实现或实现起来很昂贵。 这里有些例子。 2

2我从 Dan Abramov 的帖子“你可能不需要 Redux”和他的“React Beginner Question Thread”中收集了这些示例。

撤销重做

流行的撤消/重做功能需要系统级规划。 因为撤消/重做需要记录和重放应用程序中的每一次数据更改,所以您必须从一开始就在架构中考虑到它。 如果它是事后才完成的,则需要更改大量文件,这会导致无数错误。

撤销重做。
撤销重做。 (Beebee 的插图)(大预览)

因为 Redux 要求每一个动作都用明文描述,所以对 undo/redo 的支持几乎是免费的。 如何使用 Redux 实现撤消/重做的说明放在一个简单的页面中。

协作环境

如果您正在构建一个类似于 Google Docs 的应用程序,其中多个用户一起完成一项复杂的任务,请考虑使用 Redux。 它可能会为你做很多举重。

谷歌文档
Google Docs(Beebee 的插图)(大预览)

Redux 使得通过网络发送正在发生的事情变得非常容易。 接收另一个用户在另一台机器上执行的操作、重放更改并与本地发生的事情合并是很简单的。

乐观的用户界面

Optimistic UI 是一种改善应用程序用户体验的方法。 它使应用程序看起来在慢速网络上响应更快。 这是需要实时响应的应用程序中的一种流行策略,例如第一人称射击游戏。

乐观的用户界面
Optimistic UI(Beebee 插图)(大预览)

举个简单的例子,在 Twitter 应用中,当你点击一条推文上的心脏时,它需要请求服务器做一些检查,例如,这条推文是否还存在。 该应用程序选择作弊,而不是等待许多秒的结果! 它假设一切正常,并立即表现出一颗充满活力的心。

推特之心
Twitter 心脏(Beebee 的插图)(大预览)

这种方法有效,因为大多数时候一切都很好。 当事情不正常时,应用程序将恢复以前的 UI 更新并应用来自服务器的实际结果,例如,显示错误消息。

Redux 以与撤销和重做相同的方式支持乐观 UI。 当从服务器接收到否定结果时,它可以轻松记录、重播和恢复数据更改。

从状态持久化和启动

Redux 可以轻松地将应用程序中发生的事情保存在存储中。 稍后,即使计算机重新启动,应用程序也可以加载所有数据并从完全相同的位置继续,就好像它从未被中断过一样。

保存/加载游戏进度
保存/加载游戏进度(Beebee 插图)(大预览)

如果您使用 Redux 构建游戏,您只需要几行代码来保存/加载游戏进度,而无需更改其余代码。

真正可扩展的系统

使用 Redux,您必须“调度”一个操作来更新应用程序中的任何数据。 这种限制使得它可以连接到应用程序中发生的几乎所有方面。

您可以构建真正可扩展的应用程序,其中每个功能都可以由用户自定义。 例如,查看 Hyper,一个使用 Redux 构建的终端应用程序。 “hyperpower”扩展在光标上添加了水花并摇动了窗口。 你喜欢这种“哇”的模式吗? (也许不是非常有用,但足以打动用户)

终端应用程序 Hyper 中的“哇”模式。
终端应用程序 Hyper 中的“哇”模式。 (大预览)

时间旅行调试

在调试应用程序时能够及时旅行怎么样? 您运行应用程序,后退或前进几次以找到错误发生的确切位置,修复错误并重新播放以确认。

Redux 让开发者的这个梦想成真。 Redux DevTools 允许您通过拖动滑块来将正在运行的应用程序的进度作为 YouTube 视频来操作!

它是如何工作的? 还记得 Redux 强制执行的三个严格规则吗? 这就是魔法的秘诀。

Redux DevTools 中的时间旅行
Redux DevTools 中的时间旅行大预览

自动错误报告

想象一下:用户发现您的应用程序有问题并想要报告错误。 她煞费苦心地回忆并描述了她所做的事情。 然后,开发人员尝试手动执行这些步骤以查看错误是否再次出现。 错误报告可能含糊不清或不准确。 开发人员很难找到错误所在。

现在,这个怎么样。 用户单击“报告错误”按钮。 系统会自动将她所做的事情发送给开发人员。 开发人员单击“重放错误”按钮并观察该错误是如何发生的。 虫子被当场压扁,大家开心!

这正是使用 Redux Bug Reporter 时会发生的情况。 它是如何工作的? Redux 限制创造了奇迹。

自动错误报告
自动错误报告(Beebee 的插图)(大预览)

Redux 的缺点

Redux 强制执行的三大规则是一把双刃剑。 它们启用了强大的功能,但同时也带来了不可避免的缺点。

陡峭的学习曲线

Redux 的学习曲线相对陡峭。 理解、记住和习惯它的模式需要时间。 如果您对 Redux 和 React 都是新手,不建议同时学习它们。

“样板”代码

在许多情况下,使用 Redux 意味着编写更多代码。 通常需要触摸多个文件才能使一个简单的功能正常工作。 人们一直在抱怨他们必须用 Redux 编写的“样板”代码。

我知道,这听起来很矛盾。 我不是说Redux 可以用最少的代码实现功能吗? 这有点像使用洗碗机。 首先,你必须花时间仔细地把盘子排成一排。 直到那时你才会看到洗碗机的好处:节省实际清洗碗碟、消毒碗碟等的时间。你必须决定准备时间是否值得!

绩效处罚

由于其强制执行的限制,Redux 也可能对性能产生影响。 每当数据更改时,它都会增加一点开销。 在大多数情况下,这没什么大不了的,而且放缓并不明显。 尽管如此,当存储中有大量数据并且数据频繁更改时(例如,当用户在移动设备上快速键入时),UI 可能会因此变得迟缓。

奖励:Redux 不仅仅适用于 React

一个常见的误解是 Redux 仅适用于React。 听起来,如果没有 React,Redux 什么都做不了。 事实上,正如我们之前所讨论的,Redux 在一些重要的方面对 React 进行了补充。 这是最常见的用例。

然而,事实上,Redux 可以与任何前端框架一起工作,例如 Angular、Ember.js 甚至 jQuery,甚至是 vanilla JavaScript。 试着用谷歌搜索,你会找到这个,这个,这个,甚至这个。 Redux 的一般思想适用于任何地方!

只要您明智地使用 Redux,您就可以在许多情况下获得它的好处——不仅仅是在 React 应用程序中。

Redux 与其他前端库配合得很好
Redux 与其他前端库配合得很好。 (Beebee 的插图)(大预览)

结论

与任何工具一样,Redux 提供了权衡。 它支持强大的功能,但也有不可避免的缺点。 开发团队的工作是评估权衡是否值得并做出有意识的决定。

作为设计师,如果我们了解 Redux 的优缺点,我们将能够从设计的角度为这一决策做出贡献。 例如,也许我们可以设计 UI 以减轻潜在的性能影响? 也许我们可以提倡包含撤消/重做功能以删除大量确认对话框? 也许我们可以建议乐观的 UI,因为它以相对较低的成本改善了用户体验?

了解一项技术的优势和局限性,并据此进行设计。 我认为这就是史蒂夫乔布斯所说的“设计就是它的运作方式”。