在实际开发时,会遇到需要性能调优的问题,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来减少一次性加载的资源大小,提高加载速度。
| 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() { return ( <React.Fragment> <ChildA /> <ChildB /> <ChildC /> </React.Fragment> ) 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() { 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() { 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() { 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() { 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 = () => { console.log('this======>', this) } render() { return <div onClick={this.handleClick}>点击</div> } }
|
参考: 1.React事件处理的方式/绑定this的5种方式/事件回调函数传递参数