你知道 Redux 真正的作用远不止状态管理吗? 你是否想要了解 Redux 的工作原理? 让我们深入研究 Redux 可以做什么,它为什么做它的事情,它的缺点是什么,以及它与设计有哪些关联?
你听说过 Redux 吗?它是什么? 请不要用 Google 搜索
- 花哨的后端的东西
- 我听说过它,但我不知道它是什么,这可能是一个 React 框架
- 是一种在 React 应用中存储管理状态的更好方式
这个问题,我问过 40 多位设计师,以上是他们的经典回答。他们中的许多人都知道 Redux 与React 一起工作,它的工作是状态管理。
本文的目的就是让你对 Redux 有更全面的认知: 它能做什么?为什么它要这样设计?何时使用它?以及它与设计有哪些关联?
我的目标是帮助像你们这样的设计师。即使您以前没有写过一行代码,我认为理解 Redux仍然是可能的、有益的和有趣的。
什么是 Redux
在超高水平上,Redux 是开发人员用来简化他们工作的工具。你们很多人可能都听说过,它的工作是状态管理。稍后我将解释状态管理的含义, 此刻,我只能想让你看下面这张图:
为什么要了解 Redux
Redux 更多的是关于应用程序的内部工作而不是它的外观和感受。 这是一个有点复杂的工具,学习曲线相对陡峭,但这是否意味着我们作为设计师应该远离它?
不。我认为我们应该拥抱它。汽车设计师应该了解引擎的用途,对吗?为了成功地设计应用程序界面,设计师还应该对底层的东西有扎实的了解。我们应该了解它可以做什么,理解开发人员为什么使用它,并了解它的优势和含义。
Redux 可以做什么
开发人员在 React 应用中使用 Redux 来管理状态。这最常见的用法,Redux 改进了React(尚未)做得不好的方面。
然而,你很快就会发现 Redux 的真正功能远远不止于此,让我们从了解状态管理的真正含义开始。
状态管理
如果你不确定这个状态意味着什么,让我们用一个更通用的术语来替换它:数据。状态是不断变化的数据,状态决定在用户界面上显示什么。
状态管理是什么意思? 一般来说,我们需要在应用程序中管理三个方面的数据
- 获取和存储数据
- 将数据绑定到 UI 元素
- 改变数据
比如我们要做一个 Dribbble 的作品页面。在作业页面上我们想要展示的数据有哪些?其中包括作者的头像照片、名称、动态 GIF 图片、点赞数量、评论,以及等等。
首先,我们需要从云服务器获取所有这些数据并将其放在某个位置。接下来,我们需要实际显示数据。我们需要将这些数据分配给对应的 UI 元素,这些 UI 元素表示我们在浏览器中实际看到的内容。例如,我们将头像照片的 URL 分配给 img 标签的 src 属性:
- <img src='https://url/to/profile_photo'>
我们需要处理对数据的更改。例如,如果用户向Dribbble shot添加评论或点赞,我们需要更新相应的 HTML。
协调状态的这三个方面是前端开发的重要组成部分,React 对这项任务有不同程度的支持。有时候 React 中的内置功能运行得足够好。但随着应用程序变得越来越复杂,仅凭React 可能会更难管理它的状态。这就是为什么许多人开始使用Redux作为替代。
获取和存储数据
在React中,我们将UI分解为组件。这些组件都可以分解为更小的组件。
图片描述
如果我们的 UI 是这样构造的,那么在填充UI之前,我们什么时候获取数据以及在哪里存储数据
假设每个组件中都有一个厨师。从服务器获取数据就好比是采购所需的所有原材料以准备佳肴。
一种简单的方法是在需要的地方和时间获取和存储数据。这就像每个厨师直接从遥远的农场购买蔬菜和肉类一样。
简单方式: 每个组件各自获取自己所需要的数据
这种方法是很浪费的。即使对于相同的数据,我们也需要从多个组件多次请求服务器。厨师会浪费大量的汽油和时间来回奔波。
使用Redux,我们只获取一次数据并将其存储在一个中心位置,称为 store。然后,任何组件都可以随时使用这些数据。这就像附近有一家超市,我们的厨师可以在那里买到所有的食材。这家超市派卡车从农场大批运回蔬菜和肉类。这比让个别厨师亲自去农场效率高得多。
store 还是仅有的数据源。组件通常从 store 中获取数据,而不是其他地方。这使得 UI 保持高度统一。
Redux 将数据集中地存储起来,并将数据分配给 UI 元素
将数据绑定到 UI 元素
如果单单使用 React 的话,实际上有一种更好的方法来获取和存储数据。我们可以请我们非常善良的厨师Shotwell为他所有的厨师朋友购物。他会开一辆卡车去农场,把货物运回。我们可以从容器组件中获取数据,例如 Dribbble 示例中的 Shot 组件,并将其用作单一的数据来源。
这种方法比从每个组件获取数据的简单方法更有效。但是 Shotwell 是如何将配料传递给其他厨师的呢? 如何将数据传递给实际渲染 HTML 元素的组件? 我们将数据从外部组件传递到内部组件,就像接力棒一样,一直传递到数据到达目的地。
例如,作者头像的 URL 需要从 Shot 传递到ShotDetail、Title,传递到<img> 标签。如果我们的厨师住在公寓里,它看起来就像这样:
要将数据交付到目的地,我们必须使用路径上的所有组件,即使它们根本不需要数据。如果有很多层的话,那就太烦人了。
如果超市能送货上门呢? 使用 Redux,我们可以将任何数据插入任何组件,而不影响其他组件,就像这样
更准确地说,实际上是另一个叫做 react-redux 的库将数据提供给组件的,而并非 Redux 本身。但因为 react-redux 本身只是个连接库,并且开发者通常一起使用 Redux 和 react-redux ,因此我认为将它当做是 Redux 的好处之一是并无不妥。
使用 Redux 将数据直接提取至目标组件
注意:在React(16.3)的版本中,有一个新的 context API,它的提取数据功能几乎与 Redux 是相同的。因此,如果你的团队使用 Redux 的原因是为了提取数据,不妨认真考虑升级到 React 16.3!
改变数据
有时候,在应用程序中更新数据的逻辑可能相当复杂。它可能涉及多个相互依赖的步骤。在更新应用程序状态之前,可能需要等待多个服务器的响应。我们可能需要在不同的时间、不同的条件下更新多处 state 的状态。
如果我们没有一个适合所有逻辑的良好结构,很容易让人令人不知所措,代码也很难理解和维护。
Redux 让我们分而治之。 它提供了一种将数据更新逻辑分解为小“reducer”的标准方法。 这些 reducer 和谐地协同工作以完成复杂的动作。
Redux 的真正威力
到目前为止,Redux 看上去只是 React 的辅助工具。开发者使用它来解决 React 的某些痛点。但 React 正在快速着手解决这些问题!事实上,Redux 的作者 Dan Abramov 在几年前已经进入 Facebook 的 React 核心团队。他们一直致力于提升 React 的开发体验: context API (16.3版本发布)、更好的数据获取 API (详情请见 Dan Abramov 于2018年2月的演讲)、更好的 setState API,等等。
它会使 Redux 过时吗?
你猜怎么着? 我还没有向你展示Redux的真正力量!
Redux 迫使开发人员遵循一些严格的规则,这给 Redux 带来了强大的功能。
- 所有数据(应用程序状态)必须以明文形式描述。 你应该能够用笔在纸上写下所有数据。
- 每一个动作(数据的变更)都必须用清晰的文字来描述。你必须把你要做的事写下来,然后再做改变。你不能改变数据而不留下痕迹。在 Redux 的术语中这称之为 “派发 (dispatching) 动作”。
- 更改数据的代码必须像数学公式一样。 在相同输入的情况下,它必须返回相同的结果。 无论你运行多少次,4 的平方总是 16。
当你遵循上述原则来开发应用的话,不可思议的事情就来了。Redux 将开启许多很酷的特性,这些特性使用其他技术很难实现,或者实现起来成本很高。下面是一些例子。
我从 Dan Abramov 文章 “You Might Not Need Redux” 和 “React Beginner Question Thread.” 中收集了一些示例。
撤销、重做
流行的 撤销/重做 功能需要系统级规划。因为撤销/重做需要记录和回放应用程序中的每一次数据更改,所以你必须从一开始就在架构中考虑到这一点。如果是事后才想到的,那就需要修改很多文件,这是无数错误的根源。
正因为 Redux 需要每个动作都以文本的形式进行描述,所以可以说是天生就支持撤消/重做。这个文档中介绍了如何使用 Redux 来实现撤消/重做。
协作环境
如果你要构建类似于 Google Docs 的应用,其中多个用户在复杂任务上协同工作,请考虑使用 Redux。 它能够为你完成大量繁重的工作。。
Redux 可以非常轻松地通过网络发送正在发生的事情。 接收另一个用户在另一台机器上执行的操作,重放更改并与本地发生的操作合并是很简单的。
OPTIMISTIC UI
Optimistic UI 是 Meteor 提出来的一种前端界面快速响应用户交互的概念,之前叫 Latency Compensation,主要作用是在客户端直接响应用户的交互,而不用等信息从客户端发送到服务器,完成更新确认,再从服务器返回客户端这一个来回完成后再做响应。有点类似游戏领域里的 Dead Reckoning,在客户端离线对用户行为进行推测,达到隐藏延时和减少带宽使用的技术。
举一个简单的例子,在Twitter应用程序中,你的点赞它需要请求服务器进行一些检查,例如,该推文是否仍然存在。 Optimistic UI 的做法不是传统的转圈等待几秒,然后显示结果,而是选择欺骗用户!它事先假定所有请求都是成功的,当用户点赞时直接+1。
这种方式有效的原因在于大多数时候请求都是正常的。当请求失败是,应用只需回滚至前一个 UI 状态即可,并使用服务器响应的实际结果,例如显示错误信息。
如同撤消/重做一样,Redux 也支持 Optimistic UI。 当从服务器收到否定结果时,可以轻松记录,重放和还原数据更改。
持久化和从状态启动
Redux 可以很容易地将应用程序中发生的事情保存到本地存储中。之后,即使电脑重启,应用程序也可以加载所有数据,并从完全相同的位置继续运行,就像从未中断过一样。
如果你使用 Redux 构建游戏,则只需要几行代码来保存/加载游戏进度,而无需更改其余代码。
真正可扩展的系统
使用 Redux,你必须“dispatch”一个 action 来更新应用程序中的任何数据。 这种限制使我们可以深入了解应用程序中发生的各个方面。
你可以构建真正可扩展的应用,其中每个功能都可以由用户来自定义。例如,参考 Hyper ,这是一个使用 Redux 开发的终端应用。“hyperpower” 插件增加了光标的闪光点,并可以使窗口抖动。你是否喜欢这种 “wow” 模式呢?(或许这功能并没有什么用,但却是足够吸人眼球)
图片描述
时程调试(TIME-TRAVEL DEBUGGING)
当调试应用时能够进行时间旅行会是怎样一种体验?运行应用的过程中,随意倒退或前进几次以找到 bug 发生的确切位置,修复 bug 后重放以确认是否修复。
Redux 让开发者梦想成真。Redux 开发者工具可以使开发者通过拖拽滑动条来操纵应用的进度,就像 Youtube 视频一般。
它是如何工作的? 还记得 Redux 强制执行的三条严格规则吗? 这是它的秘诀所在。
图片描述
自动错误报告
想象一下:一个用户在你的应用程序中发现了一些错误,想要报告这个 bug。她煞费苦心地回忆和描述她所做的事情。然后,开发人员尝试手动执行这些步骤,以查看是否再次发生错误。错误报告可能是模糊的或不准确的。开发人员很难找到 bug 所在的位置。
现在,这个怎么样。 用户单击“报告错误”按钮。 系统自动将她所做的事情发送给开发人员。 开发人员单击“重播错误”按钮并观察错误是如何发生的。 bug 被当场压扁,每个人都很开心!
Redux Bug Reporter 就是这样玩的。它的工作原理呢?Redux 的限制条件让一切变成可能。
Redux 的缺点
Redux 执行的三个主要规则是一把双刃剑。它们支持强大的功能,但同时也带来不可避免的缺点。
陡峭的学习曲线
Redux 的学习曲线比较陡峭。 理解,记忆并习惯其模式需要时间。 如果你完全不会 Redux 和 React ,不推荐你两者同时学习。
“样板” 代码
在许多情况下,使用Redux意味着编写更多代码。通常需要接触多个文件才能使一个简单的功能正常工作。人们一直在抱怨他们必须用 Redux 编写的样板代码。
我知道,这听起来很矛盾。 我不是说 Redux 能够用最少的代码实现功能吗? 这有点像使用洗碗机。 首先,你得花时间仔细地排列盘子。 在此之前,你将看到洗碗机的好处:节省实际清洁餐具的时间,消毒餐具等。你必须决定准备时间是否值得!
性能损耗
由于其强制执行的限制,Redux 也可能对性能产生影响。 每当数据发生变化时,它会增加一点开销。 在大多数情况下,这不是什么大问题,而且放缓并不明显。 仍然,当存储中存在大量数据并且当数据频繁改变时(例如,当用户在移动设备上快速键入时),UI 可能因此变得缓慢。
Redux 不只是为 React 而生
一个常见的误解是 Redux 仅用于 React。 听起来Redux在没有React的情况下无法做任何事情。 事实上,正如我们之前所讨论的,Redux在几个重要方面补充了React。 React 是最最常见的 Redux 用例。
然而,事实上,Redux可以使用任何前端框架,如Angular、Ember.js 甚至jQuery 或者 普通的JavaScript。试着谷歌一下,你会发现这个,这个,这个甚至这个。Redux 的一般思想适用于任何地方
只要你明智地使用 Redux,你可以在很多情况下得到它的好处,而不仅仅是在React应用中。
总结
有不可避免的缺点。一个开发团队的职责就是进行评估,看如何进行取舍并作出明智的选择。
作为设计师,如果我们了解Redux的优势和劣势,我们将能够从设计的角度为这一决策做出贡献。 例如,我们是否可以设计用户界面以减轻潜在的性能影响? 也许我们可以提倡包含撤消/重做功能来删除大量的确认对话框? ?或许我们可以提倡 optimistic UI ,因为它能够以相对较低的代价来提升用户体验。