react性能优化

1. 使用shouldComponentUpdate避免重复渲染

以下代码,只有在count为3的时候才会触发更新

  class ClassChild extends React.Component {
    constructor() {
        super();
    }
    render() {
        console.log(‘classchild render‘);
        return <div>ClassChild</div>
    }
  }

  function FuncChild() {
    console.log(‘funcchild render‘);
    return <div>FuncChild</div>
  }
  export default class Page1 extends React.Component {
    constructor() {
        super();
        this.state = {
            count: 0,
        }
    }

    shouldComponentUpdate(nextProps, nextState) {
        if(nextState.count === 3) {
            return true;
        }
        return false;
    }

    add = () => {
        this.setState({
            count: this.state.count + 1
        });
    }
    
    render() {
        console.log(‘page1 render‘);
        return <div>
            <h1>Page1</h1>
            <button onClick={this.add}>add</button>
            <ClassChild></ClassChild>
            <FuncChild></FuncChild>
        </div>;
    }
}

大部分情况下,你可以使用React.PureComponent而不必写你自己的shouldComponentUpdate,它只做一个浅比较。但是当你比较的目标为引用类型数据,浅比较会忽略属性或状态突变的情况。

class ClassChild extends React.PureComponent {
    constructor() {
        super();
    }
    render() {
        console.log(‘classchild render‘);
        return <div>ClassChild</div>
    }
}

如果是Function组件的话,你可以使用React.memo实现同样的效果:

function Child() {
    console.log(‘funcchild render‘);
    return <div>FuncChild</div>
}
const FuncChild = React.memo(Child);

2. 属性传递优化
下面代码中,尽管ClassChild已经使用PureComponent优化了,count改变后还是会重新渲染ClassChild,是因为父组件更新后,执行render方法,ClassChild中传入的属性都是新的属性。

class ClassChild extends React.PureComponent {
    render() {
        console.log(‘classchild render‘);
        return <div>ClassChild</div>
    }
}
export default class Page1 extends React.PureComponent {
    constructor() {
        super();
        this.state = {
            count: 0
        }
    }
    
    add = () => {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return <div>
            <h1>Page1  count:{this.state.count}</h1>
            <button onClick={this.add}>add</button>
            <ClassChild style={{color: ‘red‘}}></ClassChild>
        </div>;
    }
}

因此最好将style属性提取到constructor中,如下:

class ClassChild extends React.PureComponent {
    constructor() {
        super();
        this.style = {color: ‘red‘};
    }
    render() {
        console.log(‘classchild render‘);
        return <div>ClassChild</div>
    }
}
export default class Page1 extends React.PureComponent {
    constructor() {
        super();
        this.state = {
            count: 0
        }
    }
    
    add = () => {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return <div>
            <h1>Page1  count:{this.state.count}</h1>
            <button onClick={this.add}>add</button>
            <ClassChild style={this.style}></ClassChild>
        </div>;
    }
}

3. 使用useCallback给函数做缓存

以下代码每次改变count后,尽管ClassChild已经使用了React.PureComponent, ClassChild还是会重新进行渲染,那是因为父组件Page2每次进行更新的时候,声明的add方法都是个新的方法~

import React, { useState, useCallback } from ‘react‘;
export default function Page2() {
    const [count, setCount] = useState(0);

    const add = () => {
        setCount(count+1);
    };

    return <div>
        <h1>Page2  count:{count}</h1>
        <button onClick={add}>add</button>
        <ClassChild add={add}></ClassChild>
    </div>;
}

因此需要使用useCallback对函数进行缓存,此时改变count,ClassChild不会重新渲染.(useCallback的第二个参数是依赖项,用法同useEffect,只有当依赖项变化后,才会产生新的方法)

import React, { useState, useCallback } from ‘react‘;
export default function Page2() {
    const [count, setCount] = useState(0);

    const add = useCallback(() => {
        setCount(count+1);
    }, []);

    return <div>
        <h1>Page2  count:{count}</h1>
        <button onClick={add}>add</button>
        <ClassChild add={add}></ClassChild>
    </div>;
}

4. 使用useMemo给计算的值做缓存

以下代码中,每次修改count后,expensive方法都会重新执行,这样会做很多无效的计算。

import React, { useState, useMemo } from ‘react‘;
export default function Page2() {
    const [count, setCount] = useState(0);
    const [title, setTitle] = useState(‘hhh‘);

    const add = () => {
        setCount(count+1);
    }

    const expensive = () => {
        console.log(‘expensive函数执行‘);
        var sum = 0;
        for(let i = 0; i < 100000; i++) {
            sum++;
        }
        return `title是: ${title}`;
    };

    return <div>
        <h1>Page2  count:{count}</h1>
        <button onClick={add}>add</button>
        <ClassChild add={add} expensive={expensive()}></ClassChild>
    </div>;
}

而实际上expensive方法只需要在title变化的时候才需要重新计算,因此可以使用useMemo来给计算的值做缓存

import React, { useState, useMemo } from ‘react‘;

class ClassChild extends React.PureComponent {
    constructor() {
        super();
    }
    render() {
        console.log(‘classchild render‘);
        return <div>ClassChild {this.props.expensive}</div>
    }
}

export default function Page2() {
    const [count, setCount] = useState(0);
    const [title, setTitle] = useState(‘hhh‘);

    const add = () => {
        setCount(count+1);
    }

    const expensive = useMemo(() => {
        console.log(‘expensive函数执行‘);
        var sum = 0;
        for(let i = 0; i < 100000; i++) {
            sum++;
        }
        return `title是: ${title}`;
    }, [title]);

    return <div>
        <h1>Page2  count:{count}</h1>
        <button onClick={add}>add</button>
        <ClassChild add={add} expensive={expensive}></ClassChild>
    </div>;
}

react性能优化

上一篇:安卓 学习之旅 入门


下一篇:记一次vue长列表虚拟滚动内存占用过高问题