如何评价数据流管理架构 Redux?

Redux 好在哪?用单一的State的状态树能构建大型的应用吗? 目前(rackt/redux · GitHub) star 超1W,超过了flux、reflux 等同类数据流管理架构
关注者
873
被浏览
27279

28 个回答

1月17日更新,来填坑了……

首先感谢大家的谬赞,正如 @Reducer (这名字,真不是专门为了 Redux 取的么……)所说,我的答案都是在吐槽 Redux 生态圈云云,并没有讲到 Redux 本身。

> 本来写了很长的答案,但是写着写着感觉好像变成了 Redux 入门指南……所以全数删除,只写自己对 Redux 的评价与感受。

Redux 对我来说,最耀眼的不是什么自动热部署,也不是酷炫的 redux-devtools,而是它最基础的定位 —— 一个可预测的状态容器(predictable state container)。

为什么?

在没有 Flux/Redux 之前,我们的 React 应用架构的 Model 层使用的是 Backbond.Model。对于有 Backbone 基础的人来说,整合 React 与 Backbone 的 Model 简直易如反掌。

一开始的时候我们也用的很爽,React 组件 componentDidMount 的时候初始化 Model,并监听 Model 的 change 事件,当 Model 发生改变时调用 React 组件的 setState 方法重新 render 整个组件,最后在组件 componentWillUnmount 的时候取消监听并销毁 Model。

然而当应用变得复杂的时候,一切都乱套了。有一个 userName 数据,这货到底是父组件当做 props 传下来的呢,还是自己 Model 中保存的呢,还是自己 state 中用户输入的呢?完全懵逼。

更可怕的是,产品经理改了需求,需要在一个子组件中展示一些原本在这个组件中展示的信息。很多人(包括我)为了图方便就把自己组件的 Model 直接当做 props 传给了子组件……

不敢想象,这样发展下去,Model 中一个字段发生变化触发 change 事件的时候,到底有多少 React 组件会发生 re-render。



这就是为什么,可预测,Predictable,在大型前端应用中变得那么重要!

而 Redux 是怎么做到可预测的呢?三点:

  1. 单一数据源,Single Source of Truth(也即题干中提到的 「单一的 State 状态树」)
  2. 所有数据都是只读的,要想修改数据,必须 dispatch 一个 action 来描述什么发生了改变
  3. 当处理 action 时,必须生成一个新的 state,不得直接修改原始对象
首先,单一数据源保证了整个应用中的数据都保存在同一个 Store 中,而这些数据在用在界面渲染时,都是作为 React 组件的 props。因此你在 render() 方法里想要渲染一个字段的时候,不需要再纠结它到底是 state 还是 props 还是在 Model 中,因为它肯定是 props。(当然由于 Redux 推荐的通过 combineReducers 方式 compose reducer,实际在一个组件中确定数据是来自哪个子 reducer 还是要费点神,这个目前主要依赖良好的组件结构和标准的命名规范来解决)

而对我来说,感受最直观的是第二点,因为在 Flux 之前,你已经习惯了修改一行数据自动重绘界面(如调用 this.setState)的简单与方便(估计使用 MVVM 的同学体会更深)。

突然间告诉你说,你不能直接修改数据!要想关闭这个弹框?你必须 dispatch 一个类型为 CLOSE_DIALOG 的 action,然后你的 reducer 必须在识别到这个 action 后将 store 里 dialogActive 的字段置为 false,然后你的组件必须判断当 props.dialogActive 为 false 时让弹框隐藏掉。

原本可能一行代码

this.refs.dialog.hide();

会膨胀成 Component、Reducer 和 ActionCreator 三大角色几十行代码。(具体其中的原理和操作过程这里就不细展开了)

但是,但是,这种痛苦在前期打基础的时候绝对是值得的。因为当你的应用扩展之后,你可能需要的不仅是「关闭一个对话框」,而是「当点击确定按钮时提交数据到服务器端,若成功则关闭对话框并刷新列表中的数据,若失败则弹框提示用户出错信息」。

这个时候,Redux 强大的可预测性就体现的淋漓尽致了。因为 Redux,你清楚的知道什么发生了改变(action),改变之后的数据是什么样的(store/state),以及发生了哪些改变(redux-devtool 中的 action 记录)。

最后,因为 Redux 的第三点特性(每次返回新的 state,而不是修改原始数据),配合强大的 devtool,你可以看到下面的场景:


上图就是我说的「随着应用的扩展」后一个对话框操作的全过程:
打开一个对话框 => 输入一些表单信息 => 提交 => 提交成功 => 关闭对话框 => 重新加载列表 => 列表加载成功

简直不能再清晰明了!甚至你不看我的代码,都知道我的业务逻辑是怎么执行的,再也不会出现一个 Model 更新之后不知道哪些 View 会随之更新的情况了。

以上就是我觉得 Redux 最赞的一点:可预测性

当然,实际在开发 Redux 应用的过程中是非常痛苦的,尤其是在前期,没有成熟的开发模式,生态圈不够完善,缺乏大型应用的成熟案例等等,让我们开发效率异常低下。

但是当我们慢慢摸着石头过到河中间,所以痛点都找到化解的方式之后,好像突然架起了一座桥,开发效率chua的一下就上去了。

其实我认为 Redux 最能提升的不是开发效率,而是维护效率。若干年以后,你看到上图 redux-devtools 中打印出来的信息,还是能清楚的知道这个业务过程是怎样的。


大概就是这样吧,大家还想知道什么也可以在评论中提出。




以下是原答案:
==============================================================
现在是12月20日凌晨5点43分,大约 3 个月之前我负责牵头搭建了团队里第一个基于 Redux 的项目。

是的,这是一个内部项目,所以我们上了很多新技术来试水,而 Redux 则是大家最关心的能否在项目中实际应用并产生价值的技术(这里我用了「技术」来称呼 Redux,鉴于 Flux 这种既不是库又不是框架的玩意儿,我已经不能愉快的给 Redux 定性了)。

当然项目最后成功上线,你知道对于大公司的工程师来说,项目延期是很致命的。

而关于项目本身没什么好说的,并不是简单的像博客只是渲染内容,也没有复杂到像 Gmail 一样在单页应用里承载了无数的功能。

那 Redux 呢?

这么说吧,大概 4 个小时之前,因为要新起另一个基于 Redux 的项目,我心想着升级一下依赖,就敲了一行
tnpm update --save


。。。



。。。。



。。。。



。。



结果项目到现在还没跑起来,这才过了三个月啊!!

所以我就想到了这个早就有人邀请我的问题……

大概看了一眼,版本号变化如下

redux 3.0.0 -> 3.0.5              // 看起来最正常的一个
react-redux  2.1.2 -> 4.0.1       // 什么鬼??!!
redux-devtools 2.1.2 -> 3.0.0     // 也没好到哪儿去
react-router   1.0.0-rc1 -> 1.0.2 // 1.0 正式版终于出了
redux-router 1.0.0-beta3 -> 0.1.0 // 尼玛还带版本往回退的??

不仅如此,一些 devDependencies 更是变得令人发指,好不容易研究明白了 react-hot-loader,结果作者又发明了一套 react-transform;好不容易把 redux-devtools 用熟了,结果 API 变得完全不同……

总的来说,我的内心是奔溃的。


天快亮了,先写到这儿吧。如果大家看到这里还没有被 Redux 吓到还有兴趣的话,后面再接着更新。

0314 更新:刚刚接触 Redux 的同学,可以看看这篇文章 GitHub - jasonslyvia/a-cartoon-intro-to-redux-cn: 看漫画,学 Redux。不写一行代码,轻松看懂 Redux 原理!
一套很优秀的框架。

主要优点:
1. 大幅降低了 Facebook 官方的 Flux 实现的冗余和不必要的复杂度,整体结构更为清晰和容易理解。
2. 在 react-redux的配合下,完全分离了对数据的修改操作( Action / Reducer ) 和对数据的更新( Selector ),使得开发时可以在不考虑数据修改的情况下,优先完成整体视图逻辑,然后在添加对数据的修改操作等业务逻辑时几乎不用修改视图逻辑代码
3. 单一数据源( Store )的模式使得数据管理、持久层方案选择和可调试性( Redux-Dev-Tools )都非常方便


主要缺点:
1. 对从 OOP 开发转过来的程序猿来说,函数式编程的概念接受起来需要一点门槛。
2. JavaScript 对不变对象的支持并不是特别的友好,无论是引入 immutable.js 还是 ES6 的解构语法糖有时候都觉得 Reducer 里的代码读起来有些费力,特别是对刚接触 ES6的同学来说。
3. 所有的 rackt · GitHub 旗下的框架,比如 rackt/react-router · GitHubrackt/redux · GitHub ,以及 React 本身,都流露出一种 “ 老子就是要做最牛逼的东西,向下兼容这种事情根本就不是老子考虑的问题 ” 的态度。而且很多时候不是简单的不向下兼容,而是给人一种回炉重做的感觉。针对项目开发,一定要慎重选择版本。关于这一点 @杨森 可能会有话要说,他的博客里对 react-router 的教程已经更新了 N 版,仍然多次赶不上官方的 API 变化速度。

综合结论:
Redux 非常优秀,但是目前来看,比较适合喜欢折腾、自学能力强、熟练阅读 GitHub 上的官方英文文档并于官方在 issue 里谈笑风生的开发者去学习。当然,也许再过几个月,API 真的稳定了,然后诸位大神的中文文档也普及了,就能在国内有更大的发展了吧