Skip to main content

先前技术

Redux 是一个混合产物。它和一些设计模式及技术相似,但也有不同之处。让我们来探索一下这些相似与不同。

Flux#

Redux 的灵感来源于 Flux 的几个重要特性。和 Flux 一样,Redux 规定,将模型的更新逻辑全部集中于一个特定的层(Flux 里的 store,Redux 里的 reducer)。Flux 和 Redux 都不允许程序直接修改数据,而是用一个叫作 action 的普通对象来对更改进行描述。

而不同于 Flux ,Redux 并没有 dispatcher 的概念。原因是它依赖纯函数来替代事件处理器。纯函数构建简单,也不需额外的实体来管理它们。你可以将这点看作这两个框架的差异或细节实现,取决于你怎么看 Flux。Flux 常常被表述为 (state, action) => state。从这个意义上说,Redux 无疑是 Flux 架构的实现,且得益于纯函数而更为简单。

和 Flux 的另一个重要区别,是 Redux 设想你永远不会变更你的数据。你可以很好地使用普通对象和数组来管理 state,而不是在多个 reducer 里变更数据。正确且简便的方式是,你应该在 reducer 中返回一个新对象来更新 state,同时配合 object spread 运算符提案 或一些库,Immer immutable 变更库

虽然出于性能方面的考虑,写不纯的 reducer 来变动数据在技术上是可行的,但我们并不鼓励这么做。不纯的 reducer 会使一些开发特性,如时间旅行、记录/回放或热加载不可实现。此外,在大部分实际应用中,这种数据不可变动的特性并不会带来性能问题,就像 Om 所表现的,即使对象分配失败,仍可以防止昂贵的重渲染和重计算。而得益于 reducer 的纯度,应用内的变化更是一目了然。

Elm#

Elm 是一种函数式编程语言,由 Evan Czaplicki 受 Haskell 语言的启发开发。它执行一种 “model view update” 的架构 ,更新遵循 (state, action) => state 的规则。 Elm 的 “updater” 与 Redux 里的 reducer 服务于相同的目的。

不同于 Redux,Elm 是一门语言,因此它在执行纯度,静态类型,不可变动性,action 和模式匹配等方面更具优势。即使你不打算使用 Elm,也可以读一读 Elm 的架构,尝试一把。基于此,有一个有趣的使用 JavaScript 库实现类似想法 的项目。Redux 应该能从中获得更多的启发!为了更接近 Elm 的静态类型,Redux 可以使用一个类似 Flow 的渐进类型解决方案

Immutable#

Immutable 是一个可实现持久数据结构的 JavaScript 库。它性能很好,并且命名符合 JavaScript API 的语言习惯 。

(注意尽管 Immutable.js 帮助启发了 Redux 的出现,但今天我们推荐使用 Immer 做 immutable 更新。)

Redux 并不在意你如何存储 state,state 可以是普通对象,不可变对象,或者其它类型。 为了从 server 端写同构应用或融合它们的 state ,你可能要用到序列化或反序列化的机制。但除此以外,你可以使用任何数据存储的库,只要它支持数据的不可变动性。举例说明,对于 Redux state ,Backbone 并无意义,因为 Backbone model 是可变的。

注意,即便你使用支持 cursor 的不可变库,也不应在 Redux 的应用中使用。整个 state tree 应被视为只读,并需通过 Redux 来更新 state 和订阅更新。因此,通过 cursor 来改写,对 Redux 来说没有意义。而如果只是想用 cursor 把 state tree 从 UI tree 解耦并逐步细化 cursor,应使用 selector 来替代。 Selector 是可组合的 getter 函数组。具体可参考 reselect,这是一个优秀、简洁的可组合 selector 的实现。

Baobab#

Baobab 是另一个流行的库,实现了数据不可变特性的 API,用以更新纯 JavaScript 对象。你当然可以在 Redux 中使用它,但两者一起使用并没有什么优势。

Baobab 所提供的大部分功能都与使用 cursor 更新数据相关,而 Redux 更新数据的唯一方法是分发一个 action 。可见,两者用不同方法,解决的却是同样的问题,相互并无增益。

不同于 Immutable ,Baobab 在引擎下还不能实现任何特别有效的数据结构,同时使用 Baobab 和 Redux 并无裨益。这种情形下,使用普通对象会更简便。

RxJS#

RxJS 是管理复杂异步应用非常优秀的方案。以外,还有致力于构建将人机交互作模拟为相互依赖的可观测变量的库

同时使用它和 Redux 有意义么?当然!它们配合得很好。将 Redux store 视作可观察变量非常简便,例如:

function toObservable(store) {
return {
subscribe({ next }) {
const unsubscribe = store.subscribe(() => next(store.getState()))
next(store.getState())
return { unsubscribe }
}
}
}

使用类似方法,你可以组合不同的异步流,将其转化为 action ,再提交到 store.dispatch()

问题在于: 在已经使用了 Rx 的情况下,你真的需要 Redux 吗? 不一定。通过 Rx 重新实现 Redux 并不难。有人说仅需使用一两句的 .scan() 方法即可。这种做法说不定不错!

如果你仍有疑虑,可以去查看 Redux 的源代码 (并不多) 以及生态系统 (例如开发者工具)。如果你无意于此,仍坚持使用交互数据流,可以去探索一下 Cycle 这样的库,或把它合并到 Redux 中。记得告诉我们它运作得如何!