众所周知,React的单向数据流模式致使状态只能一级一级的由父组件传递到子组件,在大中型应用中较为繁琐很差管理,一般咱们须要使用Redux来帮助咱们进行管理,然而随着React 16.3的发布,新context api成为了新的选择。react
1、Redux的简介以及缺陷chrome
Redux来源于Flux并借鉴了Elm的思想,主要原理以下图所示:redux
能够看到,Redux的数据流其实很是简单,外部事件经过actionCreator函数调用dipsatch发布action到reducers中,而后各自的reducer根据action的类型(action.type) 来按需更新整个应用的state。 api
redux设计有如下几个要点:性能优化
1. state是单例模式且不可变的,单例模式避免了不一样store之间的数据交换的复杂性,而不可变数据提供了十分快捷的撤销重作、“时光旅行”等功能。数据结构
2. state只能经过reducer来更新,不能够直接修改。app
3. reducer必须是纯函数,形如(state,action) => newState异步
redux自己是个很是纯粹的状态管理库,须要经过react-redux这个库的帮助来管理react的状态。react-redux主要包含两个部分。ide
1. Provider组件:能够将store注入到子组件的cotext中,因此通常放在应用的最顶层。函数
2. connect函数: 返回一个高阶函数,把context中由Provider注入的store取出来而后经过props传递到子组件中,这样子组件就能顺利获取到store了。
虽然redux在React项目中获得了广泛的承认与使用率,然而在现实项目中redux仍是存在着不少缺点:
1.样板代码过多:增长一个action每每须要同时定义相应的actionType而后再写N个相关的reducer。例如当添加一个异步加载事件时,须要同时定义加载中、加载失败以及加载完成三个actionType,须要一个相对应的reducer经过switch分支来处理对应的actionType,冗余代码过多。
2.更新效率问题:因为使用不可变数据模式,每次更新state都须要拷贝一份完整的state形成了内存的浪费以及性能的损耗。
3.数据传递效率问题:因为react-redux采用的旧版context API,context的传递存在着效率问题。
其中,第一个问题目前已经存在着很是多的解决方案,诸如dva、rematch以及mirror等等,笔者也造过一个相似的轮子restated这里不作过多阐述。
第二个问题首先redux以及react-redux中已经作了很是详尽的优化了,其次擅用shouldComponentUpdate方法也能够避免不少没必要要的更新,最后,也可使用一些不可变数据结构如immutable、Immr等来从根本上解决拷贝开销问题。
第三个问题属于React自身API的局限,从第三方库的角度上来讲,能作的颇有限。
2、Context API
context API主要用来解决跨组件传参泛滥的问题(prop drilling),旧的context API的语法形式以下:
能够看到,使用context api能够把DeliverComponent中的参数color直接跨越MidComponent传递到ReceiverComponent中,不须要冗余的使用props参数传递,特别是ReceiverComponent层级特别深的时候,使用context api可以很大程度上节省重复代码避免bug。
旧Context API的缺陷
旧的context api主要存在以下的缺陷:
1.代码冗余:提供context的组件要定义`childContextTypes`与`getChildContext`才能把context传下去。同时接收context的也要先定义contextTypes才能正确拿到数据。
2.传递效率:虽然功能上context能够跨层级传递,可是本质上context也是同props同样一层一层的往下传递的,当层级过深的时候仍是会出现效率问题。
3.shouldComponentUpdate:因为context的传递也是一层一层传递,所以它也会受到shouldComponent的阻断。换句话说,当传递组件的context变化时,若是其下面某一个中间组件的shouldComponentUpdate方法返回false,那么以后的接收组件将不会受到任何context变化。
为了解决旧版本的shouldComponentUpdate问题,保证全部的组件都能收到store的变化,react-redux只能传递一个getState方法给各个组件用于获取最新的state(直接传递state可能会被阻断,后面的组件将接收不到state的变化),而后每一个connect组件都须要直接或间接监听state的变化,当state发生改变时,经过内部notifyNestedSubs方法从上往下依次触发各个子组件经过getState方法获取最新的state更新视图。这种方式效率较低并且比较hack。
3、新Context API
React自16.3开始提供了一个新的context api,完全解决了旧Context API存在的种种问题。
下面是新context api(右)与使用旧context api的react-redux(左)数据流的比较:
能够看到,新的context api能够直接将context数据传递到传递到子组件中而不须要像旧context api那样级联传递。所以也能够突破shouldComponentUpdate的限制。新版的context api的定义以下:
下面是一个比较简单的应用示例:
能够看到新的context api主要包含一个Provider和Consumer对,在Provider输入的数据能够在Consumer中得到。 新context api的要点以下:
1. Provider和 Consumer必须来自同一次 React.createContext调用。也就是说 NameContext.Provider和 AgeContext.Consumer是没法搭配使用的。
2. React.createContext方法接收一个默认值做为参数。当 Consumer外层没有对应的 Provider时就会使用该默认值。
3. Provider 组件的 valueprop 值发生变动时,其内部组件树中对应的 Consumer组件会接收到新值并从新执行 children函数。此过程不受 shouldComponentUpdete 方法的影响。
4. Provider组件利用 Object.is 检测 value prop 的值是否有更新。注意 Object.is和 === 的行为不彻底相同。
5. Consumer组件接收一个函数做为 children prop 并利用该函数的返回值生成组件树的模式被称为 Render Props 模式。
4、新Context API的应用
新的Context API大大简化了react状态传递的问题,也出现了一些基于它的状态管理库,诸如:unstated、react-waterfall等等。下面咱们主要尝试使用新context api来造一个react-redux的轮子。
1. Provider
因为新的context api传递过程当中不会被shouldComponentUpdate阻断,因此咱们只须要在Provider里面监听store变化便可:
2. connect
相比较于react-redux,connect中的高阶组件逻辑就简单的多,不须要监听store变化,直接得到Provider传入的state而后再传递给子组件便可:
好了,至此整个React-redux的接口和功能都已经基本cover了,下面继续介绍一些比较重要的性能优化。
3. 性能优化 - 减小重复渲染
性能优化最大的一部分就是要减小无心义的重复渲染,当WrappedComponent的参数值没有变化时咱们应该阻止其从新渲染。能够经过手写shouldComponentUpdate方法实现,也能够直接经过PureComponent组件来达到咱们的目标:
4. 性能优化 - 减小层级嵌套
性能优化另外一个要点就是减小组件的层级嵌套,新context api在获取context值的时候须要嵌套一层Consumer组件,这也是其比旧context api劣势的地方。除此以外,咱们应该尽可能减小层级的嵌套。所以在前一个性能优化中咱们不该该再次嵌套一个PureComponent,取而代之的是,咱们能够直接在Cunsumer中实现一个memory机制,实现代码以下:
下面是先后chrome开发人员工具中组件层级的对比,能够看到嵌套层级成功减小了一层,两层嵌套是新context api的局限,若是要保持react-redux的接口模式则没法再精简了。