从设计的角度看Redux
你知道 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应用中。
总结