太原房产网

当前位置: 首页 >资讯

探索Redux40版本迭代论基础谈展望

来源: 作者: 2019-11-10 00:57:02

Redux 在几天前(2018.04.18)发布了新版本,6 commits 被合入 master。从诞生起,到如今 4.0 版本,Redux 保持了使用层面的平滑过渡。同时前不久, React 也从 15 升级到 16 版本,开发者并不需要作出太大的变动,即可”无痛升级”。但是在版本迭代的背后很多有趣的设计值得了解。Redux 此次升级一样如此。

本文将从此次版本升级展开,从源代码改动入手,进行分析。通过后文内容,相信读者能够在 JavaScript 基础层面有更深认识。

本文支持前端初学者学习,同时更适合有 Redux 源码浏览经验者,核心源码并不会重复分析,更多将聚焦在升级改动上。

改动点总览

这次升级改动点一共有 22 处,最主要体现在 TypeScript 使用、CommonJS 和 ES 构建、关于 state 抛错三方面上。对于工程和配置的改动,我们不再多费笔墨。主要从代码细节入手,基础入手,侧重分析以下几处改动:

中间件 ApI dispatch 参数处理;

applyMiddleware 改动;

bindActionCreators 对 this 透明化处理;

dispatching 时,对 state 的冻结;

plain Object 类型判断;

话不多说,我们直接进入正题。

applyMiddleware 参数处理

这项改动由 Asvarox 提出。熟悉 Redux 源码中 applyMiddleware.js 设计的读者一定对 middlewareApI 并不陌生:对于每个中间件,都可以感知部分 store,即 middlewareApI。这里简单展开一下:

创建一个中间件 store:

我们看,applyMiddleware 是个三级 curry 化的函数。它将陆续获得了三个参数,第一个是 middlewares 数组,[mid1 mid2 mid3 ...],第二个是 Redux 原生的 createStore,最后一个是 reducer;

applyMiddleware 利用 createStore 和 reducer 创建了一个 store,然后 store 的 getState 方法和 dispatch 方法又分别被直接和间接地赋值给 middlewareApI 变量。middlewares 数组通过 map 方法让每个 middleware 带着 middlewareApI 这个参数分别履行一遍。执行完后,取得 chain 数组,[f1 f2 ... fx ...fn],接着 compose 将 chain 中的所有匿名函数,[f1 f2 ... fx

... fn],组装成一个新的函数,即新的 dispatch,当新 dispatch 执行时,[f1 f2 ... fx ... fn] 将会从右到左依次执行。以上解释改动自:pure render 专栏。

好了,把中间件机制扼要解释以后,我们看看这次改动。故事源于 Asvarox 设计了一个自定义的中间件,这个中间件接收的 dispatch 需要两个参数。他的”杰作”就像这样:

对比传统编写中间件的套路:

我们能清晰地看到他的这类编写方式会有什么问题:在原有 Redux 源码基础上,actionCreator 参数后面的 args 将会丢失。因此他提出的改动点在:

如果你好奇他为何会这样设计自己的中间件,可以参考 #2501 号 issue。我个人认为对需求来讲,他的这类”奇葩”方式,可以通过其他手段来规避;但是对 Redux 库来说,将 middlewareApI.dispatch 参数展开,确切是更合适的做法。

此项改动我们点到为止,不再钻牛角尖。应该学到:基于 ES6 的不定参数与展开运算符的妙用。虽然一直在说,一直在提,但在真正开发程序时,我们仍然要时刻注意,并养成良好习惯。

基于此,一样的改动也体现在:

这项改动由 jimbolla 提出。

bindActionCreators 对 this 透明化处理

Redux 中的 bindActionCreators,达到 dispatch 将 action 包裹起来的目的。这样通过 bindActionCreators 创建的方法,可以直接调用 dispatch(action) (隐式调用)。可能很多开发者并不常用,所以这里稍微展开,在 action.js 文件中, 我们定义了两个 action creators:

在另外一文件 SomeComponent.js 中,我们便可以直接使用:

这样一来,我们在子组件 Child 中,直接调用 newAction.action1 就相当于调用 dispatch(action1),如此做的好处在于:没有 store 和 dispatch 的组件,也可以进行动作分发。

一般这个 ApI 运用不多,最少笔者不太经常使用。因此上面做一个简单介绍。有经验的开发中一定不难猜出 bindActionCreators 源码做了甚么,连带着这次改动:

我们看这次改动,对 actionCreator 使用 apply 方法,明确地进行 this 绑定。那么这样做的意义在哪里呢?

我举一个例子,想象我们对原始的 actionCreator 进行 this 绑定,并使用 bindActionCreators 方法:

我们应该期望 boundAction 和 action 一致;且 boundAction.this 和 uniqueThis 一致,都等同于 action.this。这如此的期望下,这样的改动无疑是必须的。

对 state 的冻结

Dan Abramov 认为,在 reducer 中使用 getState() 和 subscribe() 方法是一种反模式。store.getState 的调用会使得 reducer 不纯。事实上,原版已经在 reducer 履行进程中,禁用了 dispatch 方法。源码如下:

同时,这次修改在 getState 方法以及 subscribe、unsubscribe 方法中进行了一样的冻结处理:

笔者认为,这样的做法毫无争议。显式抛出异常无意是合理的。

plain Object 类型判断

plain Object 是一个非常有趣的概念。这次改动围绕判断 plain Object 的性能进行了激烈的讨论。最终将引用 lodash isplainObject 的判断方法改为 ./utils/isplainObject 中自己封装的做法:

简单来说,plain Object:

指的是通过字面量形式或new Object()情势定义的对象。

Redux 这次使用了以下代码来进行判断:

如果读者对上述代码不理解,那末需要补一下原型、原型链的知识。简单来讲,就是判断 obj 的原型链有几层,只有一层就返回 true。如果还不理解,可以参考下面示例代码:

而 loadash 的实现为:

isObjectLike 源码:

baseGetTag 源码:

根据 timdorr 给出的比较结果,dispatch 方法中:

这一组 benchmark 引发的讨论自然少不了,也引出来了 Dan Abramov。笔者对此不发表任何意见,感兴趣的同学可自行研究。从结果上来看,摒除了部分对 lodash 的依赖,在性能表现上说服力增强。

展望和总结

提到 Redux 发展,自然离不开 React,React 新版本一经推出,极受追捧。尤其是 context 这样的新 ApI,某些开发者认为将逐步取代 Redux。

笔者认为,围绕 React 开发应用,数据状态管理始终是一个极其重要的话题。但是 React context 和 Redux 并不是完全对峙的。

首先 React 新特性 context 在大型数据运用的前提下,并不会减少模版代码。而其 provider 和 Consumer 的逐一对应特性,即 provider 和 Consumer 必须来自同一次 React.createContext 调用(可以用 hack 方式解决此”局限”),恍如 React 团队对于此特性的发展方向设计主要体现在小型状态管理上。如果需要实现更加灵活和直接的操作,Redux 也许会是更好的选择。

其次,Redux 丰富的生态以及中间件等机制,决定了其在很大程度上具有不可替代性。毕竟,已使用 Redux 的项目,迁移本钱也将是极大的,最少需要开发中先升级 React 以支持新版 context 吧。

最后,Redux 作为一个”发布订阅系统”,完全可以脱离 React 而单独存在,这样的基因也决定了其后天与 React 本身 context 不同的性征。

我认为,新版 React context 是对 React 本身”短板”的长线补充和完善,未来大概率也会有所打磨调整。Redux 也会进行一系列迭代,但就如同这次版本升级一样,将趋于稳定,更多的是细节上调剂。

退一步讲,React context 的确也和 Redux 有千丝万缕的联系。任何类库或者框架都具有其短板,Redux 同样也如此。我们完全可以使用新版 React context,在使用层面来规避 Redux 的一些劣势,模仿 Redux 所能做到的一切。犹如 didierfranc 的 react-waterfall,国内@方正的

Rectx,都是基于新版

React context 的解决方案。

最后,我很赞同@诚身所说:选择用什么样的工具从来都不是决定一个开发团队成败的关键,根据业务场景选择恰当的工具,并利用工具反过来约束开发者,终究到达控制整体项目复杂度的目的,才是促进一个开发团队不断提升的核心动力。

没错,真正对项目起到决定性作用的还是是开发者本身,完善基础知识,提升开发技能,让我们从

Redux 4.0 的改动看起吧。

greenviagra多少钱

西地那非价格

西地那非合成路线

金戈西地那非片

相关推荐