关于 ReactJS 生命周期以外的性能提升奇技淫巧

关于 ReactJS 生命周期以外的性能提升奇技淫巧


前言

总所周知 ReactJS 在页面渲染以及重绘方面有着较为成熟的实现方式,虚拟 DOM 的提出以及基于高效的 diff 算法相比其他轻量化前端框架有着较高的页面性能,以及生命周期的实现为我们开发提供了很多便利,但成也萧何败也萧何,当我们在开发是不注意生命周期的控制,组件还是会对页面性能造成一定压力的。

 

不过本文考虑到大家都是都比较忙,关于性能不准备从生命周期方面入手,给大家介绍一些实用且易上手的性能提升方法, 和 ReactJS 在开发时要注意的一些大坑。

 

正文

巧用状态( state )

 

老王准备做一个表格,数据量大且动态更新,表头动态更新,老王的常规做法如下

//未优化版本
    import React, { Component } from 'react';
    import { Table } from 'tinper-bee';
    const defaultColumns = [...];

    const defaultData = [...];

    class View extends Component {
        constructor(props) {
            super(props);
            this.state = {
                loading: false,
                columns: defaultColumns,
                data: defaultData
            };
            setInterval(()=>{
                if(this.state.loading) return;
                this.setState({
                    loading: true
                }, () => {
                    this.loadTableData();
                });
            }, 1000);
        }

        loadTableData = () =>{
            Ajax({...}).then(request = >{
                let { data, columns } = request;
                this.setState({ 
                    loading: false, 
                    data, columns
                });
            });
        }
        render() {
            let { columns, data, loading } = this.state;
            return <Table loading={loading} columns={columns} data={data} />;
        }
    }

上述写法对一般场景几乎没什么性能问题,但如果数据量及大,且更新频率快的话,就会出现资源损耗较高的情况,具体原因则是老王每次在刷新数据时都经过 setState 方法来修改数据,我们知道经过生命周期的所有状态 ReactJS 都会经过一次浅比较,来决定是否需要重绘,比较对象 object( data 和 columns )则会逐条比较,每次都是新的则每次都会比较,根据大二《数据结构》中关于时间复杂度的公式得出,数组逐条比较的时间复杂度最小为 O(n^2),最多为 O(n^n),空间复杂度无算。

 

作为一个有理想的程序猿,这种情况是不能忍的,必须将复杂度降为O(1),才能对得起老王聪明绝顶的大脑。优化开始。

 

//优化版本
    import React, { Component } from 'react';
    import { Table } from 'tinper-bee';
    const defaultColumns = [...];

    const defaultData = [...];

    class View extends Component {
        constructor(props) {
            super(props);
            this.state = {
                loading: false
            };
            
            /*--------关键代码------------*/
            this.columns = defaultColumns;
            this.data = defaultData;
            
            /*-------------------------*/
            setInterval(()=>{
                if(this.state.loading) return;
                this.setState({
                    loading: true
                }, () => {
                    this.loadTableData();
                });
            }, 1000);

        }

        loadTableData = () =>{
            Ajax({...}).then(request = >{
                let { data, columns } = request;

                /*--------关键代码------------*/
                this.columns = columns;
                this.data = data;
                /*-------------------------*/

                this.setState({ 
                    loading: false
                });
            });
        }
        render() {
            let { loading } = this.state;
            return <Table loading={loading} columns={this.columns} data={this.data} />;
        }
    }

优化结束。

上述过程其实就是将所有 object 作为变量缓存,而非状态,因为 ReactJS 鬼知道他会怎么处理这些对象,所以我们可以将它提炼出来,在通过其他简单变量状态的变化强制重绘,而对象为引用地址状态变化时则会引起页面的内容的变化。

 

这中写法比较偷懒,不过也不失为一种快捷实用简单的方法。大家可以根据自己的实际情况采纳。

 

render 不是保姆,别给她太多工作

render 方法是状态组件的最终出口,我们状态的修改需要经过 render 方法来体现到界面上,他的主要职责就是重绘,但很多时候我们只将状态(state)的作为了一个记录使用,又在 render 内做了大量数据计算和逻辑运算。请看代码:

//未优化版本
    import React, { Component } from 'react';
    import { Table } from 'tinper-bee';
    const defaultColumns = [...];

    const defaultData = [...];

    class View extends Component {
        constructor(props) {
            super(props);
            this.state = {
                loading: false,
                columns: defaultColumns,
                data: defaultData
            };
            setInterval(()=>{
                if(this.state.loading) return;
                this.setState({
                    loading: true
                }, () => {
                    this.loadTableData();
                });
            }, 1000);
        }

        loadTableData = () =>{
            Ajax({...}).then(request = >{
                let { data, columns } = request;
                this.setState({ 
                    loading: false, 
                    data, columns
                });
            });
        }
        onRowDoubleClick(record){
            ...
        }
        render() {
            /*--------问题代码------------*/
            let { columns, data, loading } = this.state;
            //不要逻辑判断
            if(!data || !data.length){
                data = defaultData;
            }
            if(!columns|| !columns.length){
                data = defaultColumns;
            }

            //不要创建方法
            function onSelect (record) {
                ...
            }
            return <Table 
                        style={{
                            color: 'red'
                        }}
                        loading = {loading} 
                        columns = {columns} 
                        data = {data} 
                        onSelect = {onSelect}
                        //不要创建方法
                        onRowClick = {(record) =>{
                            ...
                        }}
                        //不要零时指定作用域
                        onRowDoubleClick = {this.onRowDoubleClick.bind(this)}
                        //这个更二
                        getClick = {this.onRowDoubleClick.bind(this, 'isClick')}
                />;
        }
    }

如果没有特殊要求,最好不要按上述方式写,render 内部的工作遵循一个原则,什么业务代码都不要写,他的功能只用做重绘。

 

总结

这次描述的两个场景其实就是两个原则,一、状态(state)只做状态控制不做数据记录;二、render 只做绘制不做任何业务处理,遵循着两个原则,基本可以应付大多数性能问题。

小试牛刀,不成文章,请大家多提意见,我一般不看。告辞。

最后送上写代码背景音乐 ,我是一个有理想的程序猿。
http://music.163.com/song?id=516851025&userid=277108995
http://music.163.com/song?id=544355238&userid=277108995

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值