资讯专栏INFORMATION COLUMN

重新设计 Redux

kidsamong / 2667人阅读

摘要:相关状态父组件传递给子组件的状态。外部状态状态是可以从视图库中移出来的,然后可以使用提供者消费者模式把状态重新连接回视图库。重新设计在我看来,重写是有其必要性的,至少有以下个方面可以改进得更友好。

Redux 学习起来很困难?写起代码来很啰嗦?
一起来看看 Rematch 的作者对 Redux 的思考和简化吧~

原文:《Redesigning Redux》, Shawn McKay

都过了这么多年了,状态管理的问题难道不应该早就被解决了么?
个人直觉,开发者似乎都承认一个潜规则,那就是状态管理问题似乎比想象的更复杂。在本文,我们将一起探讨你可能已经遇到的问题:

我们真的需要一个状态管理库么?

Redux 的流行是否实至名归?为什么?

我们是否能够提出更好的状态管理方案?如果是,怎么做?

真的需要状态管理库?

前端开发并不仅仅是左右移动像素点。开发的真正艺术是掌握如何管理状态。
简单粗暴的答案是:状态管理是复杂的,但是并非那么复杂。

以基于组件的视图框架/库为例,比如 React,让我们来看看我们有哪些选择:

1. 组件状态

存在于单个组件内部的状态。在 React 中,就是指使用 setState 来更新的 state

2. 相关状态

父组件传递给子组件的状态。在 React 中,就是指通过属性传递给子组件的 props

3. 供给状态

保存在根提供者中、通过组件树传递给消费者的状态。在 React 中,对应于 context API

大多数的状态都是存在于视图中的,因为它是用来反映用户界面的。那么,对于反映底层数据和逻辑的其它状态,又属于谁呢?

如果把所有的状态都填塞到视图中,那么就严重违背了关注点分离原则。它会把你牢牢地绑在一个 JS 视图库中,使得代码难以测试。更有甚者,可能会为你带来大麻烦,因为你必须持续不断的思考和调整状态的存储之处。

由于架构设计的改变,状态管理会随之变得复杂,并且通常很难判断哪些组件需要哪些状态。最简单的做法是,在根组件上包含所有状态。如果真要这么做的话,那么选用下一种方式会更好。

4. 外部状态

状态是可以从视图库中移出来的,然后可以使用提供者/消费者模式把状态重新连接回视图库。

Redux 也许是最流行的状态管理库。它在过去的 2 年时间里取得了巨大的流行度。那么,为什么一个简单的库会获得如此之多的关注呢?

是因为它的性能更高么?其实并不是。实际上,它反而让每一个必须处理的回调变得更慢了些。

那是因为它更简单?也决定不是。

论简单的话,那么纯 JS 才是。正如 TJ 所说:

那为什么大家不直接使用 global.state = {} 呢?

为什么使用 Redux

本质上,Redux 跟 TJ 所说的是同一件事,只不过 Redux 封装了一些管道工具而已。

在 Redux 中,我们不能直接修改状态。修改状态的唯一方式是分发(Dispatch)一个动作(Action)到管道中,管道会自动根据动作去更新状态。

从上图可以看到,管道的中有 2 个监听器集合:中间件(Middleware)和订阅(Subscription)。中间件一些是监听动作的函数,用来处理类似日志记录、开发工具和发起服务请求等功能。订阅则是一些用来广播状态变更的函数。

最后,合成器(Reducer)函数负责把状态变更拆分成更小、更模块化、更容易管理的代码块。

和使用一个全局对象相比,Redux 确实简化了开发过程。

综上,我们可以把 Redux 看成是一个全局对象,该对象不仅提供了状态更新前/后钩子,而且能够以简单的方式合成新状态。

不觉得 Redux 过于复杂么?

的确过于复杂。在平时的开发过程中,有一些不可否认的迹象,可以用来判断框架 API 是否需要改进。这些迹象可以归纳为下面这个公式:

其中,节省的时间是指使用该框架来开发所消耗的时间,学习的时间则是阅读框架文档、学习教程和掌握新概念的总时间。

Redux 本身是个精简的库,但是其学习曲线却很陡峭。虽然有不少开发者能够克服深入学习函数式编程的困难并从 Redux 获益良多,但是也有很多开发者望而却步,宁愿重新使用 jQuery。

在 jQuery 中,你并不需要理解什么是 “comonad”,也没必要理解通过函数组合来管理状态。

任何框架或者库的目的都应该是把复杂的事物抽象得更加简单。

当然我这么说并不是想指责 Redux 的作者 Dan Abramov 。Redux 在其刚诞生初期就已经变得非常流行,没有足够的时间来精雕细琢。

我们怎么能随便重构一个已经被成千上万开发者使用的库呢?

我们又怎么能合理发布会影响到不计其数项目的重大变更呢?

没人可以。但是我们可以提供更好的支持,比如完善文档、推广视频教程和扩大社区等。在这些方面,Dan Abramov 确实做得很棒。

又或者,还有另一种方式可以改变现状。

重新设计 Redux

在我看来,重写 Redux 是有其必要性的,至少有以下 6 个方面可以改进得更友好。

1. 初始化过程

让我们来看看一个基本的 Redux 初始化过程,如下图左边所示:

很多开发者走到这里一步就开始止步了,简直是一看三不知。什么是 chunkcompose 又是什么鬼?一个函数还能这么使用?

如果 Redux 是基于配置而不是函数组合的话,那么像右边那样的初始化过程明显看起来更加合理。

2. 简化状态合成器

Redux 中的状态合成器能够使用一个 switch 来代替多个不必要的 switch

假如状态合成器是根据动作的类型来匹配的,那么我们可以用逆向思维,把合成器变成一个接受 stateaction 两个参数的纯函数。也许还可以更简单些,我们可以把动作规划化,并且只传递状态和一个数据载荷。

3. 使用 Async/Await 代替 Thunks

在 Redux 中,Thunks最通用的做法就是用来创建异步动作。从多角度来看,这种用法更像是一个聪明的黑客所采用的用法,而不是一种官方推荐的用法。我们一步一步来看:

首先分发了一个动作,然而实际上却是一个函数而不是期望的对象

Thunk 中间件检查每一个动作,看它是否是一个函数

如果是函数,那么中间件调用该函数,同时把 dispatchgetState 方法传参进去

真的需要这样么?把一个简单的动作在运行时识别为对象、函数甚至是 Promise,这难道不是糟糕的实践么?

如上图右边所示,难道我们就不能只使用 async/await ?

4. 两种类型的动作

如果我们认真想想的话,确实有两种类型的动作:

合成器动作(Reducer action): 触发合成器然后改变状态

副作用动作(Effect action):触发一个异步动作。这可能也称为合成器动作,但是异步函数其实并没有直接改变任何状态。

因此,把这两种类型的动作区分开来会更有用,同时也不容易与 Thunks 搞混。

5. 不再需要定义动作类型变量

为什么我们的标准实践要把动作生成器和状态合成器区分开来呢?能否只用其中一个呢?改变其中一个又是否会影响到另一个?

在我看来,动作生成器和状态合成器就是硬币的两个面。

const ACTION_ONE = "ACTION_ONE" 可以说是把动作生成器和状态合成器分开的冗余产物。如果我们把这两者合二为一,那么就不会有那么多文件专门导出这些类型字符串了。

6. 合二为一的合成器

按照使用方式,把 Redux 中所涉及的概念进行合并分组,那么我们可以得出下面这个更简单的模式。

在这种模式中,状态合成器是可以自动确定与之对应的动作生成器。因为,此时状态合成器可以自动变成动作生成器。

通过简单的命名约定,以下行为都会变得可预测:

如果状态合成器命名为 increment,那么动作类型就是 increment。更好的做法是加上命名空间: count/increment

每个动作都通过 payload 属性来传递数据。

这样的话,对于 count.increment,我们就可以自动的从状态合成器推导出动作生成器。

好消息:更棒的 Redux

以上的通电就是我们创建 Rematch 的原因。

Rematch 对 Redux 进行了封装,提供更简单的 API,但又不失任何可配置性的特点。

下面是一个完整的使用例子:

我已经把 Rematch 应用在生成环境中有好几个月了。作为小白鼠,我的体验是:

状态管理从未变得如此简单、高效。

Redux 并没有被抛弃,而且也不应该被抛弃。
只是,我们应该以更低的学习成本,更少的样板代码和更少的认知成本,来拥抱 Redux 背后的简单哲学。

赶紧试一试 Rematch 吧!
万一一不小心你就爱上它了呢?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95498.html

相关文章

  • Redux概念之一: Redux简介

    摘要:应用这说明并不是单指设计给用的,它是独立的一个函数库,可通用于各种应用。在数据流的最后,要触发最上层组件的,然后进行整体的重新渲染工作。单纯在的对象上是没有办法使用,要靠额外的函数库才能这样作,这是一定要使用类似像这种函数库的主要原因。 Redux的官网中用一句话来说明Redux是什么: Redux是针对JavaScript应用的可预测状态容器 这句话虽然简短,其实是有几个涵义的: ...

    cjie 评论0 收藏0
  • Rematch: Redux重新设计

    摘要:沿着管道有两组侦听器中间件和订阅。中间件是可以侦听传入的动作的函数,支持诸如,或侦听器之类的工具。将视为一个带有更新前更新后钩子的全局对象,以及能够以简单的方式合成新状态。应将两者视为一体,并且不再需要文件导出类型的字符串。 难道现在状态管理不是一个可以解决的问题吗?直观地说,开发人员似乎知道一个隐藏的事实:状态管理的使用似乎比需要的更困难。在本文中,我们将探讨一些你可能一直在问自己的...

    Taste 评论0 收藏0
  • Flux,Vuex,Redux

    摘要:是一种前端状态管理架构思想,专门解决软件的结构问题。基于的设计思想,出现了一批前端状态管理框架。他们给出了一些库用于实现的思想,并在的基础上做了一些改进。在这些框架里,当前最热门的莫过于和了。 Flux Flux是一种前端状态管理架构思想,专门解决软件的结构问题。 基于Flux的设计思想,出现了一批前端状态管理框架。他们给出了一些库用于实现Flux的思想,并在Flux的基础上做了一些改...

    anonymoussf 评论0 收藏0
  • 精读《重新思考 Redux

    摘要:本周精读内容是重新思考。数据流对数据缓存,性能优化,开发体验优化都有进一步施展的空间,拥抱插件生态是一个良好的发展方向。 本周精读内容是 《重新思考 Redux》。 1 引言 《重新思考 Redux》是 rematch 作者 Shawn McKay 写的一篇干货软文。 dva 之后,有许多基于 redux 的状态管理框架,但大部分都很局限,甚至是倒退。但直到看到了 rematch,总算...

    IntMain 评论0 收藏0
  • 关于Flux,Vuex,Redux的思考

    摘要:关于的思考是一种前端状态管理架构思想,专门解决软件的结构问题。他们给出了一些库用于实现的思想,并在的基础上做了一些改进。在这些框架里,当前最热门的莫过于和了。 关于Flux,Vuex,Redux的思考 Flux是一种前端状态管理架构思想,专门解决软件的结构问题。基于Flux的设计思想,出现了一批前端状态管理框架。他们给出了一些库用于实现Flux的思想,并在Flux的基础上做了一些改进。...

    jsbintask 评论0 收藏0

发表评论

0条评论

kidsamong

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<