React性能优化

在实际开发时,会遇到需要性能调优的问题,React本身提供了许多优化方案,现将常用进行总结。

1.使用纯组件PureComponent

  PureComponent是一个继承自Component的子类,会自动加载shouldComponentUpdate函数。当组件需要更新时,会对组件的props和state进行一次浅比较。如果props和state都没有发生变化,组件不会重新渲染,从而提升性能。如果prop和state每次都会变,那么PureComponent的效率还不如Component,进行浅比较也需要时间。当PureComponent忽略重渲染时,不仅会影响自身,还会影响其子元素。因此,使用纯组件的最佳方式是显示组件,它既没有子组件,也不依赖于应用程序的全局状态。

2.使用React.memo()

  当React.memo()包装一个组件时,React会记住渲染的输出,然后跳过不必要的渲染。它是一个高阶函数,与React.PureComponent类似,但是一个函数组件而非一个类。React.memo()可接受2个参数,第一个参数为纯函数的组件,第二个参数用于对比props控制是否刷新,与shouldComponentUpdate()功能类似。

3.使用shouldComponentUpdate生命周期事件

  在react开发中,state状态改变,会将所有组件重新渲染,有重复render的问题。shouldComponentUpdate是在重新渲染组件之前触发的生命周期事件,函数执行默认返回true,结果为true才会触发render钩子。我们可以在该事件中通过自定义逻辑,以决定是否调用组件的render函数。这个函数将nextState和nextProps作为输入参数,可将其与当前props和状态做对比,以决定是否需要重新渲染。

4.使用lazy、Suspense实现懒加载

  懒加载通过对组件进行分割打包成多个chunk来减少一次性加载的资源大小,提高加载速度。

1
2
3
4
5
6
7
8
9
10
11
12
import React, { lazy, Suspense } from 'react'
const MyButton = lazy(() => import('./MyButton'))

export default class MyComponent extends React.Component {
render() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MyButton />
</Suspense>
)
}
}
  • 通过lazy()函数动态import需要懒加载的组件
  • Suspense来包裹懒加载的组件进行加载,可以通过设置fallback实现加载中效果
  • import的组件目前只支持export default的形式导出
  • 路由也可以使用lazy、Suspense实现懒加载

5.使用React.Fragment避免额外标记

  React中的一个常见模式是一个组件返回多个元素。Fragment片段不会向组件引入任何额外标记,但它仍然为两个相邻标记提供父级,因此满足在组件顶级具有单个父级的条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React from 'react'
import ChildA from './ChildA'
import ChildB from './ChildB'
import ChildC from './ChildC'

export default class Table extends React.Component {
render() {
// Fragment 支持key属性
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
)
// <> </> 短语法,看起来像空标签,不支持key或其他属性
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
)
}
}

6.涉及函数this指向,最佳方法是在函数自定义阶段使用箭头函数绑定,有以下优点

  • 箭头函数会自动绑定到当前组件的作用域中,不会被call改变
  • 它避免了在render中通过bind或者箭头函数绑定的性能问题
  • 它避免了在构造函数中预先声明绑定时,可能出现大量重复的代码

  在react中绑定this有以下5中方法
  ①使用React.createClass,React 16版本的发布官方已经将改方法从React中移除,15及以下版本可用

1
2
3
4
5
6
7
8
9
10
import React from 'react'
const App = React.createClass({
handleClick() {
// 指向App 组件本身
console.log('this======>', this)
}
render() {
return <div onClick={this.handleClick}>点击</div>
}
})

  ②render方法中直接使用bind,组件每次执行render将会重新分配函数,将会影响性能

1
2
3
4
5
6
7
8
9
10
import React from 'react'
export default class App extends React.Component {
handleClick() {
// 指向App 组件本身
console.log('this======>', this)
}
render() {
return <div onClick={this.handleClick.bind(this)}>点击</div>
}
}

  ③render方法中使用箭头函数,每次render调用时都会创建一个新的事件处理函数,会给组件带来额外的开销

1
2
3
4
5
6
7
8
9
10
import React from 'react'
export default class App extends React.Component {
handleClick() {
// 指向App 组件本身
console.log('this======>', this)
}
render() {
return <div onClick={(e) => this.handleClick(e)}>点击</div>
}
}

  ④构造函数中bind,一个组件中有很多的事件函数时,这种在构造函数中绑定this的方法会显得繁琐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react'
export default class App extends React.Component {
constructor(props) {
super(props)
// 预先绑定构造函数
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
// 指向App 组件本身
console.log('this======>', this)
}
render() {
return <div onClick={this.handleClick}>点击</div>
}
}

  ⑤在定义阶段使用箭头函数绑定

1
2
3
4
5
6
7
8
9
10
import React from 'react'
export default class App extends React.Component {
handleClick = () => {
// 指向App 组件本身
console.log('this======>', this)
}
render() {
return <div onClick={this.handleClick}>点击</div>
}
}

参考: 1.React事件处理的方式/绑定this的5种方式/事件回调函数传递参数


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!