如何有效地提高react渲染效率--深复制,浅复制,immutable原理

24 篇文章 0 订阅
23 篇文章 0 订阅

1. 性能意义:保持state不变这个约束引导我们使用局部更新对象的方法,这样会可以非常有效地提高react或其他显示框架的渲染效率。我们先来看看为了保持数据不变性,要怎么对state做更新,以我们的苹果篮子state为例:

例子:通知开始摘苹果:apple/BEGIN_PICK_APPLE

为了保证每个版本的state不变性,我们有两种实现方式:“深复制”,“浅复制”。我们来看两种模式的内部原理:

深复制方式:有人会这样想:“保持state的不变性很容易,只需要深复制一个state, 让后在新的state要怎么修改就怎么修改,不ok了吗?”,如下就是深复制

这种方式是一种很低级保持不变性的方式:

  1. 深复制操作运行效率低

  2. 没有为渲染环节提供提高渲染效率的铺垫

它只是简单迎合保持数据不变性的约束,虽然有一定调试意义,但是,不但没有提高程序的性能,反而降低了程序的总体性能!没有实践意义。

浅复制方式:浅复制模式只对内部数据发生变化的引用做更新,如下

“state” 对象的内部数据发生变化,所以创建新的state引用;而apples array 内部数据不发生变化,所以就不对该引用做更新!在这个操作中,这种浅复制的方法运行效率比较高,而且其简单地实现了数据不变性,为调试带来方便,同时,也是更重要的,这种浅复制的方式极大地提高了视觉组件渲染阶段的运行效率!我们来对比一下:当用户点击摘苹果时,如果使用“深复制”,渲染程序需要重新遍历整个state对象树来做视觉更新,而使用浅复制来实现数据不变性时,渲染程序只需要遍历state对象的一级子节点即可,而不需要对apples array 做遍历,性能大大地提高。尤其是当苹果很多的时候,两种方式的性能差距是非常明显的。

备注:在react组件里面,要开启条件更新这个生命周期函数 shouldComponentUpdate, 才会对把这个性能提高点释放出来,类似这样:

...
shouldComponentUpdate(nextProps) {
    return nextProps.state != this.props.state;
}
...

下面我们再给出 “吃苹果” reducer 进行浅复制的例子:

现在大家应该理解了用浅复制实现数据不变性的原理和意义了,下面我们来看具体的代码实现。

我们的代码用 es6 编写,这里要用到 es6 两个非常方便的特性:

  1. Obejct.assign() 方法,该方法用于产生新的对象

  2. 延展操作符 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 任务的介绍就结束啦~

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值