zoukankan      html  css  js  c++  java
  • react总结之性能优化(fragment,pureComponent,memo)

      在平时的项目开发中,性能问题是比较大的问题,小型项目中还看不出来太大的变化 ,当项目的规模达到一定程度时,性能问题就显得重要,下面分别来介绍几种性能优化的方法。

      一、Fragment

      fragment可以聚合一个子元素列表,并不需要在DOM中增加额外的节点 ,避免多写一层view,多层级的嵌套。React.Fragment看起来像是空的JSX标签。

    return (
        <React.Fragment>
            <Component />
        </React.Fragment>
    )
    //Fragment短语法
    return (
        <>
            <Component />
        </>
    ) 

      二、shouldComponentUpdate

      组件更新生命周期中的shouldComponentUpdate(SCU)从字面上来理解它问的是是否需要进行更新,默认返回的true进行更新。但在组件渲染的过程中,有时候并没有用到props/state或者是在父组件重新渲染时子组件的props/state并没有发生改变,这时render得到的是和之前一样的虚拟DOM,所以我们要使用SCU来进行组件是否需要更新的判断。通过这个API我们可以拿到改变前后的props/state,手动的检查状态是否发生了更新,再根据实际的变量情况决定是否需要进行重新渲染。

    import React from 'react'
    import reactDOM from 'react-dom'
    class Counter extends React.Component {
        render() {
            console.log('Counter render')
            return <div>当前总和是:{this.props.count}</div>
        }
    }
    class App extends React.Component {
        constructor(props) {
            super(props);
            this.inputRef = React.createRef();
            this.state = { count: 0 };
        }
        render() {
            console.log('App render')
            return (
                <div>
                    <Counter count={this.state.count} />
                    <input ref={this.inputRef} />
                    <button onClick={this.add} >+</button>
                </div>)
        }
        add = () => {this.setState({ count: this.state.count + parseInt(this.inputRef.current.value) })}
        //shouldComponentUpdate有两个参数:nextProps,nextState.
        //用上一次的props/state和这一次的props/state进行比较,如果相同返回false 不需要更新,如果不一样,则更新
        shouldComponentUpdate(nextProps, nextState) {
            // return true;//默认值
            if (this.state.count !== nextState.count) {
                return true;
            }
            return false;
        }
    
    }
    reactDOM.render(<App />, document.getElementById('root'))

      二、PureComponent

      如果多处要用到SCU时,react中还有一个类似的组件PureComponent,在组件更新对props/state进行一次浅比较来判断是否进行更新操作实现了SCU。在项目中,如果定义了SCU,无论组件是否是PureComponent,它都会执行SCU来判断是否进行更新,如果组件中没有SCU,那就会判断这个组件是否是PureComponent,是的话会对新旧props/state进行浅比较,不一致的话则会触发更新操作。

    import React from 'react'
    import reactDOM from 'react-dom'
    class Counter extends React.PureComponent {
        render() {
            console.log('Counter render')
            return <div>当前总和是:{this.props.count}</div>
        }
    }
    class App extends React.PureComponent {
        constructor(props) {
            super(props);
            this.inputRef = React.createRef();
            this.state = { count: 0 };
        }
        render() {
            console.log('App render')
            return (
                <div>
                    <Counter count={this.state.count} />
                    <input ref={this.inputRef} />
                    <button onClick={this.add} >+</button>
                </div>)
        }
        add = () => { this.setState({ count: this.state.count + parseInt(this.inputRef.current.value) }) }
        //shouldComponentUpdate有两个参数:nextProps,nextState.
        //用上一次的props/state和这一次的props/state进行比较,如果相同返回false 不需要更新,如果不一样,则更新
    }
    reactDOM.render(<App />, document.getElementById('root')

      浅比较就是对栈内存中的数据进行比较,我们手写一个PureComponent,从而来更好的理解浅比较。当参数为基本数据类型或者是同一个引用对象那直接返回true;判断两个不同引用类型对象是否相同,先比较其length属性,如这一步不同,则返回false;再通过Object.keys获取对象的属性进行比较。

    import React, { Component, createRef } from 'react'
    import ReactDOM from 'react-dom';
    class PureComponent extends React.Component {
        shouldComponentUpdate(newProps) {//判断是否需要更新
            return !shallowEqual(this.props, newProps);
        }
    }
    //浅比较,只比较第一层
    function shallowEqual(obj1, obj2) {
        if (obj1 === obj2) {//obj1和obj2引用地址完全相等的情况
            return true;
        }
        if (typeof obj1 != 'object' || obj1 == null || typeof obj2 != 'object' || obj2 == null) { //判断两个数据都为object的情况
            return false;
        }
        let key1 = Object.keys(obj1);
        let key2 = Object.keys(obj2)
        if (key1.length != key2.length) {//属性数量不一样,直接返回false
            return false;
        }
        //没有进行递归操作,它不是深比较,只是比较一层而已
        for (let key of key1) { //循环obj1属性的数组,将其和obj2进行比较
            if (!obj2.hasOwnProperty(key) || obj1[key] != obj2[key]) {//如果obj2没有这个key,或者是obj1的key的值不等于obj2的key的值
                return false
            }
        }
        return true;//最后返回true  
    }
    class Counter extends PureComponent {
        render() {
            console.log('Counter render')
            return <p>当前的总和是:{this.props.counter.count}</p>
        }
    }
    class App extends Component {
        constructor(props) {
            super();
            this.inputRef = createRef();
            this.state = { counter: { count: 0 } }
        }
        add = () => {
            let oldState = this.state;
            let amount = parseInt(this.inputRef.current.value);
            //这里的判断如果amount==0,那counter还是老的counter对象没有改变
            let newState = { ...oldState, counter: amount == 0 ? oldState.counter : { count: oldState.counter.count + amount } };
            this.setState(newState);
        }
    
        render() {
            console.log('App render')
            return (
                <div>
                    <Counter counter={this.state.counter} />
                    <input ref={this.inputRef} />
                    <button onClick={this.add} >+</button>
                </div>)
        }
    }
    ReactDOM.render(<App />, document.getElementById('root'))

      三、immutable.js

      在使用PureComponent时,它只是进行了浅层比较,如果props传入的对象嵌套的层级太多,可能会引起props或者是state引用地址未发生变化从而导致shouldComponentUpdate返回false,未触发render函数,没有渲染组件的情况,这时我们就可以用到immutable.js库来进行优化。

      immutable.js是一个持久性数据结构的库。Immutable Date 是一旦创建就不能再被更改的数据,对immutable对象的任何修改或添加删除操作都会返回一个新的immutable对象。它在使用旧数据创建新数据时,要保证旧数据可用且不变,还要避免deepCopy深拷贝带来的性能上的大量损耗,所以immutable使用了结构共享,即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点进行共享。

      Immutable.js常用的API有以下几个is()、Map()、List()等等,下面来介绍几个常用的API

      is(map1,map2): 对map1和map2进行比较。和js中对象的比较不同,在js中比较两个对象比较的是地址,但是在immutable中比较的是这个对象hashCode和valueOf,只要两个对象的hashCode相等,值就是相同的,避免了深度遍历,提高了性能。

    import { Map, is } from 'immutable'
    let map1 = Map({ a: { aa: 1 }, b: 2, c: 3 });
    let map2 = Map({ a: { aa: 1 }, b: 2, c: 3 });
    console.log(map1 === map2);//false
    Object.is(map1, map2); //false
    is(map1, map2);//true

      Map():用来创建一个新的Map对象;get():获取值 ;set():设置值

    let { Map} = require('immutable');
    let map1 = Map({ a: { aa: 1 }, b: 2, c: 3 });
    let map2 = map1.set('b', 50);
    console.log(map1.b, map2.b);//这是不一样的,map2中的b进行了修改
    console.log(map1.get('a') === map2.get('a'));//true 这是一样的,a的值它们是进行共享的如何将immutable.js和PureComponent结合起来使用。

      把immutable.js用到项目中,代码如下:

    import React, { Component, createRef,PureComponent } from 'react'
    import ReactDOM from 'react-dom';
    import {Map} from 'immutable'
    class Counter extends PureComponent {
        render() {
            console.log('Counter render')
            //用get取值
            return <p>{this.props.counter.get('count')}</p>
        }
    }
    class App extends Component {
        constructor(props) {
            super();
            this.inputRef = createRef();
            //使用Map
            this.state = { counter: Map({ count: 0 }) }
        }
        add = () => {
           //这样的话就要用到immutable.js这个库
           let oldState = this.state;
           let amount = parseInt(this.inputRef.current.value);
           oldState.counter = oldState.counter.set('count', oldState.counter.get('count') + amount)//每次set后会返回一个新的对象赋值给oldState.counter
           this.setState(oldState)
        }
        render() {
            console.log('App render')
            return (
                <div>
                    <Counter counter={this.state.counter} />
                    <input ref={this.inputRef} />
                    <button onClick={this.add} >+</button>
                </div>)
        }
    }
    ReactDOM.render(<App />, document.getElementById('root'))

       四、memo

      上面是类组件的优化,那函数组件呢?函数组件采用React.memo()进行优化提高组件性能。React.memo是一个高阶组件,它仅检查props的变化,如果组件在相同的props的情况下,那可以通过将组件包裹在React.memo中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。在默认情况下它和PureComponent一样都是进行浅比较的。

    import React, { Component, createRef,  PureComponent } from 'react'
    import ReactDOM from 'react-dom'
    //函数组件
    function Count(props) {
        console.log('Count render')
        return <p>当前数的总和是:{props.count.count}</p>
    }
    //使用memo
    // let MemoCount = React.memo(Count);
    
    //重写memo
    let MemoCount = memo(Count);
    function memo(FuctionComponent){
        return class extends PureComponent{
            render(){
                return <FuctionComponent {...this.props} />
            }
        }
    }
     class App extends Component {
        constructor(props) {
            super();
            this.inputRef = createRef();
            this.state = {
                count: { count: 0 },
            }
        }
        add = () => {
            let oldState = this.state;
            let amount = parseInt(this.inputRef.current.value);
            let newState = { ...oldState, count: amount == 0 ? oldState.count : { count: oldState.count.count + amount } };
            this.setState(newState)
        }
        render() {
            console.log('App render')
            return (
                <div>
                    <MemoCount count={this.state.count} />
                    <input ref={this.inputRef} />
                    <button onClick={this.add} >+</button>
                </div>)
        }
    }
    ReactDOM.render(<App />, document.getElementById('root'))

     

      

      

  • 相关阅读:
    JDBC 基本语法总结
    MySQL 基本语法
    mysql学习第三天练习(日期和时间函数)
    mysql学习第三天练习(流程控制函数)
    mysql学习第三天笔记
    mysql学习第二天函数
    mysql学习第一天select
    mysql学习第一天
    Java石头剪刀布小游戏
    Java万年历
  • 原文地址:https://www.cnblogs.com/davina123/p/13774190.html
Copyright © 2011-2022 走看看