抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

好久没学react了,今天记录一下react中关于优化性能的组件

react中的两个高性能组件:

  1. PureComponent 类组件中的
  2. memo 函数式组件中的

两个用于性能优化的hook:

  1. useCallback 记忆函数
  2. useMemo 记忆值(也可以记忆函数,将会取代useCallback)

PureComponent

在react中,当组件里的state被改变的时候(通过setState),所有组件就会重新渲染。有时可能该组件所依赖的状态没有改变,或者该组件没有使用到状态,但react依然会重新渲染所有组件,这显然不是我们想要的。

在普通Component组件中,可以通过shouldComponentUpdate钩子来判断该组件是否需要重新渲染,大概这样:

shouldComponentUpdate(nextProps,nextState){
    console.log('当前现有的props值为'+ this.props.msg);
    console.log('即将传入的props值为'+ nextProps.msg);
    return this.props.msg !== nextProps.msg
}

这不够优雅!

有没有更优雅的操作?

有!

通过PureComponent,让原本继承Component的组件继承PureComponent,这样,这个组件就成了一个“纯组件”(土翻译)

import { Componet, PureComponent } from 'react';
class Demo extends PureComponent{
    render(){
        return (<p>Hello PureComponent</p>);
    }
}

使用PureComponent之后,组件内部渲染时会自动判断组件内部需要的Props有没有改变,就无需我们再判断了。

但这只能“浅判断”,也就是只能判断值,对于对象这种,只能判断地址。

memo

既然在类组件可以通过shouldComponentUpdate或PureComponent来判断是否需要渲染,那在函数式组件中应该怎样解决这个问题呢?

答案就是使用高阶组件(HOC):memo

类似这样:

import { memo } from 'react';
const Demo = (props) => {
    return (<h1>demo 组件内的props:{props.msg}</h1>)
}
const memoDemo = memo(Demo)
class App extends Component{
    render(){
        return (
        	<memoDemo msg="hiahia~"></memoDemo>
        )
    }
}

这样,只有当组件内依赖的值发生改变之后,其才会重新渲染,达到了和PureComponent一样的效果。

注:memo也只能浅判断

useCallback

下面看这样一个例子:

// memoDemo 省略同上
const App = () => {
    const myFn = () => {
        console.log("hiahia~");
    }
    return (
    	<memoDemo fn={myFn}></memoDemo>
    )
}

对于函数式组件中传递函数props的时候,因为每次重新渲染都会再走一遍构造函数,所创建的函数地址也会发生变化,这时候就要用到useCallback来解决这个问题。

import { useCallback } from 'react';
// memoDemo 省略同上
const App = () => {
    const myFn = useCallback(() => {
        console.log("hiahia~");
    },[]);
    return (
    	<memoDemo fn={myFn}></memoDemo>
    )
}

useCallback函数的第一个参数是要包裹的函数,第二个写需要关注的依赖,当该依赖改变后,其包裹的函数也会重新更新。那我这里只想让他的函数固定不变,那就传个空数组进去好了。

useMemo

useMemo是用来解决复杂的数据类型的hook,写成如下格式就等同于useCallback,useCallback是这种写法的语法糖:

import { useMemo } from 'react';
// memoDemo 省略同上
const App = () => {
    const myFn = useMemo(() => ()=> {
        console.log("hiahia~");
    },[]);
    return (
    	<memoDemo fn={myFn}></memoDemo>
    )
}

useMemo保存的是计算后的结果,在回调函数的return中,可以写任意数据类型,不单单写函数,可以用来处理复杂的数据类型。

import { useMemo,useState } from 'react';
// memoDemo 省略同上
const App = () => {
    const [userName, setUsername] = useState('孙泽辉');
    const [nums, setNums] = useState(99999);
    const Fibonacci = useMemo(() => {
        // 计算第nums个斐波那契数
        let i = 1,a = 1,b = 1;
        do{
            [a, b] = [b, a + b]
        }while(i>nums);
        return b;
    },[nums]);
    return (
        <input value={userName} onChange={event => setUserName(event.target.value)}></input>
        <input value={nums}></input>
    	<h2>{Fibonacci}</h2>
    )
}

因为计算斐波那契数列很耗时,用户修改用户名的时候不需要重新计算斐波那契数列,可以使用上次保存好的结果,只有当用户改变要计算的第几个斐波那契数列的时候才需要重新计算。

useLayoutEffect

useLayoutEffect和useEffect最大的不同就是useLayoutEffect会在浏览器渲染之前执行,相当于类组件中的componentDidMount/componentDidUpdate。

他们之间的执行顺序是:

在首次渲染时控制台的打印顺序为 App 渲染 → Com 渲染 → useLayoutEffect 执行… → App componentDidMount → useEffect 执行…。

而点击按钮更改状态触发重渲染时,打印顺序为 App 渲染 → Com 渲染 → useLayoutEffect 销毁… → useLayoutEffect 执行… → App componentDidUpdate → useEffect 销毁… → useEffect 执行…。
————————————————
版权声明:本文为CSDN博主「_Boboy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43720095/article/details/104967975

useLayoutEffect会阻塞浏览器渲染,切勿执行同步耗时的操作。

参考文章:

何时使用useLayoutEffect? - SegmentFault 思否

评论