1. 性能意义:保持state不变这个约束引导我们使用局部更新对象的方法,这样会可以非常有效地提高react或其他显示框架的渲染效率。我们先来看看为了保持数据不变性,要怎么对state做更新,以我们的苹果篮子state为例:
例子:通知开始摘苹果:apple/BEGIN_PICK_APPLE
为了保证每个版本的state不变性,我们有两种实现方式:“深复制”,“浅复制”。我们来看两种模式的内部原理:
深复制方式:有人会这样想:“保持state的不变性很容易,只需要深复制一个state, 让后在新的state要怎么修改就怎么修改,不ok了吗?”,如下就是深复制
这种方式是一种很低级保持不变性的方式:
-
深复制操作运行效率低
-
没有为渲染环节提供提高渲染效率的铺垫
它只是简单迎合保持数据不变性的约束,虽然有一定调试意义,但是,不但没有提高程序的性能,反而降低了程序的总体性能!没有实践意义。
浅复制方式:浅复制模式只对内部数据发生变化的引用做更新,如下
“state” 对象的内部数据发生变化,所以创建新的state引用;而apples array 内部数据不发生变化,所以就不对该引用做更新!在这个操作中,这种浅复制的方法运行效率比较高,而且其简单地实现了数据不变性,为调试带来方便,同时,也是更重要的,这种浅复制的方式极大地提高了视觉组件渲染阶段的运行效率!我们来对比一下:当用户点击摘苹果时,如果使用“深复制”,渲染程序需要重新遍历整个state对象树来做视觉更新,而使用浅复制来实现数据不变性时,渲染程序只需要遍历state对象的一级子节点即可,而不需要对apples array 做遍历,性能大大地提高。尤其是当苹果很多的时候,两种方式的性能差距是非常明显的。
备注:在react组件里面,要开启条件更新这个生命周期函数 shouldComponentUpdate, 才会对把这个性能提高点释放出来,类似这样:
...
shouldComponentUpdate(nextProps) {
return nextProps.state != this.props.state;
}
...
下面我们再给出 “吃苹果” reducer 进行浅复制的例子:
现在大家应该理解了用浅复制实现数据不变性的原理和意义了,下面我们来看具体的代码实现。
我们的代码用 es6 编写,这里要用到 es6 两个非常方便的特性:
-
Obejct.assign() 方法,该方法用于产生新的对象
-
延展操作符 Spread operator : ...
大家可以稍微看一下文档,或者看我下面的例子就知道其用法了:
// apple basket reducer
export default (state = {
isPicking: false,
newAppleId: 1,
apples: [
{
id: 0,
weight: 235,
isEaten: false
}
]
}, action) => {
let newState ;
switch (action.type) {
case 'apple/BEGIN_PICK_APPLE':
newState = Object.assign({}, state, {
isPicking: true
});
return newState;
case 'apple/DONE_PICK_APPLE':
newState = Object.assign({}, state, {
apples: [
...state.apples,
{
id: state.newAppleId,
weight: action.payload,
isEaten: false
}
],
newAppleId: state.newAppleId + 1,
isPicking: false
})
return newState;
case 'apple/FAIL_PICK_APPLE':
//这里只是简单处理
newState = Object.assign({}, state, {
isPicking: false
});
return newState;
case 'apple/EAT_APPLE':
newState = Object.assign({}, state, {
apples: [
...state.apples.slice(0, action.payload),
Object.assign({}, state.apples[action.payload], { isEaten: true }),
...state.apples.slice(action.payload + 1)
]
})
return newState;
default:
return state;
}
};
大家会发现这种浅复制操作在开发的过程会复杂一点,于是就有了 immutable.js 这个专门处理不变性数据的库(也是facebook出品),它可以使用类似赋值的方式生成浅复制的不变性数据,下面来看看它怎么简化我们的开发:
我们用 apple/EAT_APPLE
这个reducer来说明。
这是原来的 reducer:
...
case 'apple/EAT_APPLE':
newState = Object.assign({}, state, {
apples: [
...state.apples.slice(0, action.payload),
Object.assign({}, state.apples[action.payload], { isEaten: true }),
...state.apples.slice(action.payload + 1)
]
})
return newState;
...
这是使用 immutable.js 库的reducer :
import { fromJS } from 'immutable';
...
case 'apple/EAT_APPLE':
return fromJS(state).setIn(['apples',action.payload,'isEaten'], true).toJS();
...
用了immutable.js后,轻松一行代码搞定!如果团队约定 state 都用 immutable 内部的数据类型,就可以连 fromJS 和 toJS 的转化都省了,超级方便!
到这里, reducer 任务的介绍就结束啦~