react性能优化

本文介绍React应用中的性能优化策略,包括减小文件体积的方法及如何避免不必要的组件重新渲染。通过合理利用shouldComponentUpdate和PureComponent,结合Immutable.js,实现更高效的UI更新。

React使用几种巧妙的技术来最大限度地减少更新UI所需的昂贵的 DOM 操作的数量

1.文件引入大小

下面三种小方法本质都是进行代码的压缩,使文件比较小。

使用生产版本

开发环境我们使用: npm run dev 启动服务。
但是生产版本应使用: npm run build 将代码打包
package.json中:
"scripts": {
    "build": "webpack --mode production",
    "dev": "webpack-dev-server --mode development"
  },
这样做的好处:1.代码会被压缩 2.引入的包都是压缩后的版本  
复制代码

单文件构建

如果使用单文件构建这种方式,应该引入压缩后的版本:
<script src="https://unpkg.com/react@15/dist/react.min.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.min.js"></script>
复制代码

webpack

new webpack.DefinePlugin({
  'process.env': {
    NODE_ENV: JSON.stringify('production')
  }
}),
new webpack.optimize.UglifyJsPlugin()
当使用使用生产版本构建的时候就不需要配置webpack了。
复制代码

2.避免重新渲染

此图为react内部组件更新原理图。

当组件的 props 和 state 改变时,React 通过比较新返回的元素 和 之前渲染的元素 来决定是否有必要更新DOM元素。当二者不相等时,则更新 DOM 元素

scu表示shouldComponentUpdate(组件是否需要更新),红色表示返回true需要更新,绿色表示false不需要更新。当c1需要更新时,查看c2和c3的scu。c2的scu为false,就不需要寻找c4 c5的scu为什么状态。c3的scu为true,查看c6, c7, c8的scu,只有c6的scu为true。所以只需要更新c1,c3, c6。从而避免了其他组件重新渲染。

  • 2.1 shouldComponentUpdate

有一个组件功能为:一个输入框,一个添加按钮。输入框输入内容,点击添加按钮。输入框下列表添加上输入框内容。

export default class Todos extends PureComponent {
    constructor(props) {
        super(props);
        this.state = { todos: [] };
    }
    handleClick = () => {
        let todo = this.todo.value;
        this.state.todos.push(todo);
        this.setState({ todos:this.state.todos });
    }
    
    //重写此方法以减少重新渲染
    shouldComponentUpdate(nextProps, nextState) {
        if (nextState.todos === this.state.todos) {
            return false;//老的状态对象和新的状态对象如果是同一个吧,则不用再渲染了
        } else {
            return true;
        }
    }
    
    render() {
        return (
            <div>
                <input ref={input => this.todo = input} /><button onClick={this.handleClick}>+</button>
                <ul>
                    {
                        this.state.todos.map((todo, index) => (<li key={index}>{todo}</li>))
                    }
                </ul>
            </div>
        )
    }
}
复制代码

重写shouldComponentUpdate减少组件的刷新频率:如果我们只需要让todos改变的时候刷新组件,如果todos没有改变就不刷新组件。这样判断减少了组件的刷新次数。

如果没有重写shouldComponentUpdate方法,只要调用了setState,组件就会重新刷新,即使更新内容为空:

handleClick = () => {
        this.setState({});
    }
    //render方法依然被调用,组件依然重新渲染。
复制代码

但是下面这种方法永远不能使组件重新刷新:

handleClick = () => {
        let todo = this.todo.value;
        this.state.todos.push(todo);
        this.setState({ todos:this.state.todos });
    }
    
    //重写此方法以减少重新渲染
    shouldComponentUpdate(nextProps, nextState) {
        if (nextState.todos === this.state.todos) {
            return false;//老的状态对象和新的状态对象如果是同一个吧,则不用再渲染了
        } else {
            return true;
        }
    }
复制代码

因为:老的数组push一个todo,然后赋值给新的数组。再拿老的数组和新数组比较。比较结果必然相等啊(两个相同地址的对象必然相等)。所以只能判断为相等,不能使组件更新。

与shouldComponentUpdate方法同理,可以使用PureComponent代替Component(就不需要上面原理就是重写shouldComponentUpdate方法方法了)。原理就是重写shouldComponentUpdate方法。

class PureComponent extends Component {
    shouldComponentUpdate(nextProps, nextState) {
        //循环下一个新的属性对象的每一个属性,判断新的属性值和旧的属性是不是同一个
        //重点强调 ,这个比较属性属于浅比较
        // 如果把这里改为深比较,那么CPU占用高
         for (let prop in nextProps) {
             if (nextProps[prop] !== this.props[prop]) {
                 return true;
             }
         }
         for (let prop in nextState) {
             if (nextState[prop] !== this.state[prop]) {
                 return true;
             }
         }
         return false;
    }
}
复制代码

解决方法:以上for in 循环比较为浅比较。只是比较的引用地址,如果为一个数组,向数组中添加了一个元素,依然不能比较出改变。再次修改handleClick方法,将todos解构出来,使之不为同一个数组。

handleClick = () => {
        let todo = this.todo.value;
        this.state.todos.push(todo);
        this.setState({ todos:[...this.state.todos] });
    }
复制代码

此时面对两个问题:1.如果使用同一个todos,则不能比较出状态的改变。2.如果解构出来一个新的数组,则数组不能复用,重新使用了一个对象,则浪费性能,占用内存(如果todos有100万个)。

有没有两全其美的方法?

imuutable

import {  List } from 'immutable';
handleClick = () => {
        let todo = this.todo.value;
        let todos = List(this.state.todos);//将老的对象变为immutable对象
        todos = todos.push(todo);
        this.setState({ todos });
    }
复制代码

使用imuutable中的is方法(判断两个对象的属性值是否相等),再次优化shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState) {
        return !(is(newProps, this.props) && is(nextState, this.state));
        //循环下一个新的属性对象的每一个属性,判断新的属性值和旧的属性是不是同一个
        //重点强调 ,这个比较属性属于浅比较
        // 如果把这里改为深比较,那么CPU占用高
        // for (let prop in nextProps) {
        //     if (nextProps[prop] !== this.props[prop]) {
        //         return true;
        //     }
        // }
        // for (let prop in nextState) {
        //     if (nextState[prop] !== this.state[prop]) {
        //         return true;
        //     }
        // }
        // return false;
    }
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值