likes
comments
collection
share

关于React的性能优化

作者站长头像
站长
· 阅读数 7

一、React的diff算法

1.1 React更新机制

首先看一下Raect的渲染流程:

关于React的性能优化

再看一下React的更新流程:

关于React的性能优化

1.2 React更新流程

React在state或props发生改变时,调用render函数,渲染一颗新的虚拟DOM树;

React需要基于这两颗不同的树之间的差异来判别如何有效的更新UI:

  • 同层之间相互比较,不会跨节点比较;
  • 不同类型的节点,产生不同的树结构(如果以div标签为根的树改为了p标签,那div的所有子元素都重新渲染,产生p为根的树);
  • 开发中,可以通过key来指定哪些节点在不同的渲染下保持稳定;

关于React的性能优化

如图所示,只会进行同层之间的比较

tip:为什么不建议使用index作为key?

在列表渲染时,使用index作为key是没有性能提升的,因为diff算法在对key进行比较时,如果key为index索引,那么在列表中间插入一个值时,后面的所有index都会发生改变,原来元素的index和新的元素的index自然就会不同,即使他们是同一个元素,但是key为index,index不一致就会进行重渲染而不是移动原来的元素到新的位置。

二、SCU

在render函数执行时存在这么一个问题:

每当调用setState进行数据更新时,就会执行render函数来重新渲染我们的DOM结构,如果我们只更新了父组件中的数据,但是子组件内依赖的props并没有发生改变,只要调用render,就都会重新渲染,即使子组件的数据没有发生改变,这就会导致性能低下。

shouldComponentUpdate生命周期

React提供了一个生命周期方法 shouldComponentUpdate(简称为SCU),这个方法接受参数,并且要有返回值:

该方法有两个参数:

  • 参数一:nextProps:props修改之后新的props属性;
  • 参数二:nextState:state修改之后新的state属性。

这个生命周期函数可以决定render函数是否执行,返回值是一个boolean类型:

  • 如果它返回的是一个true,就执行render;
  • 如果返回的是false,那render不会执行,就不会重新渲染DOM。
  • 默认返回的是true,也就是说只要state发生改变,就会调用render函数来重新渲染DOM

PrueComponent

但是如果每次都在组件内部使用 shouldComponentUpdate, 那么开发起来真的是好麻烦啊;

  • 而使用shouldComponentUpdate的目的不外乎是当state中的数据发生改变时,来决定 shouldComponentUpdate 返回的是true还是false,来决定组件要不要重新渲染。
  • React已经提供了一个方法实现这点,只需要将class组件继承自 PureComponent类就好了。

demo如下:App组件有Home和Banner两个子组件,Home有App传递的props,Banner没有porps

import React, { PureComponent } from 'react'
import Home from './Home'
import Banner from './Banner'

export class App extends PureComponent {
  constructor() {
    super()
    this.state = {
      message: "Hello World"
    }
  }

  chanegText() {
    this.setState({
      message: "你好,世界"
    })
  }

  render() {
    console.log("App render")
    const { message } = this.state
    return (
      <div>
        <h2>{message}</h2>
        <button onClick={e => this.chanegText()}>修改文本</button>
        <Home message={message} />
        <Banner />
      </div>
    )
  }
}

export default App
import React, { PureComponent } from 'react'

export class Home extends PureComponent {
  constructor(props) {
    super(props)
  }
  render() {
    console.log("Home render")
    const { message } = this.props
    return (
      <div>
        <h2>Home</h2>
        <div>{message}</div>
      </div>
    )
  }
}

export default Home
import React, { PureComponent } from 'react'

export class Banner extends PureComponent {
  constructor() {
    super()
  }
  render() {
    console.log("Banner render")
    return (
      <div>Banner</div>
    )
  }
}

export default Banner

memo 高阶组件

函数式组件如何实现PrueComponent那样在props没有改变时,不要重新渲染DOM树呢?;

只需要使用高阶组件memo就可以了:

import { memo } from "react"

const PreFile  = memo(function() {
  return (
    <div>
      PreFile
    </div>
  )
})

export default PreFile

React官网:zh-hans.react.dev/reference/r…

三、useSelector的第二个参数shallEqual

React-Hooks