React 组件性能优化最佳实践
React 组件性能优化的核心是减少渲染真实 DOM 节点的频率,减少 Virtual DOM 比对的频率。如果子组件未发生数据改变不渲染子组件。
组件卸载前进行清理操作
以下代码在组件挂载时会创建一个interval组件销毁后清除定时器,间隔1秒会触发渲染count+1
,组件销毁后如果不清除定时器它会一直消耗资源
import React, { useState, useEffect } from "react"
import ReactDOM from "react-dom"
const App = () => {
let [index, setIndex] = useState(0)
useEffect(() => {
let timer = setInterval(() => {
setIndex(prev => prev + 1)
console.log('timer is running...')
}, 1000)
return () => clearInterval(timer)
}, [])
return (
)
}
export default App
每次数据更新都会触发组件重新渲染,这里的优化为:组件销毁清理定时器
类组件使用纯组件PureComponent
什么是纯组件
纯组件会对组件输入数据进行浅层比较,如果当前输入数据和上次输入数据相同,组件不会重新渲染
什么是浅层比较
比较引用数据类型在内存中的引用地址是否相同,比较基本数据类型的值是否相同。
为什么不直接进行 diff 操作, 而是要先进行浅层比较,浅层比较难道没有性能消耗吗
和进行 diff 比较操作相比,浅层比较将消耗更少的性能。diff 操作会重新遍历整颗 virtualDOM 树, 而浅层比较只操作当前组件的 state 和 props。
import React from "react"
export default class App extends React.Component {
constructor() {
super()
this.state = {name: "张三"}
}
updateName() {
setInterval(() => this.setState({name: "张三"}), 1000)
}
componentDidMount() {
this.updateName()
}
render() {
return (
)
}
}
class RegularComponent extends React.Component {
render() {
console.log("RegularComponent")
return {this.props.name}
}
}
class PureChildComponent extends React.PureComponent {
render() {
console.log("PureChildComponent")
return {this.props.name}
}
}
组件挂载以后会有一个定时器间隔1秒设置一次name
,我们可以看到RegularComponent
一直在渲染,即使数据没有发生变化也会渲染。PureChildComponent
只有一次渲染,因此使用纯组件会对props
state
进行进行比较,数据相同不会重新渲染。
shouldComponentUpdate
纯组件只能进行浅层比较,要进行深层比较,使用 shouldComponentUpdate,它用于编写自定义比较逻辑。
返回 true 重新渲染组件,返回 false 阻止重新渲染。
函数的第一个参数为 nextProps, 第二个参数为 nextState。
import React from "react"
export default class App extends React.Component {
constructor() {
super()
this.state = {name: "张三", age: 20, job: "waiter"}
}
componentDidMount() {
setTimeout(() => this.setState({ job: "chef" }), 1000)
}
shouldComponentUpdate(nextProps, nextState) {
if (this.state.name !== nextState.name || this.state.age !== nextState.age) {
return true
}
return false
}
render() {
console.log("rendering")
let { name, age } = this.state
return {name} {age}
}
}
即使继承了Component
的组件定时器一直修改数据也不会触发重新渲染
纯函数组件使用React.memo
优化性能
memo 基本使用
将函数组件变为纯组件,将当前 props 和上一次的 props 进行浅层比较,如果相同就阻止组件重新渲染。
import React, { memo, useEffect, useState } from "react"
function ShowName({ name }) {
console.log("showName render...")
return {name}
}
const ShowNameMemo = memo(ShowName)
function App() {
const [index, setIndex] = useState(0)
const [name] = useState("张三")
useEffect(() => {
setInterval(() => {
setIndex(prev => prev + 1)
}, 1000)
}, [])
return (
{index}
)
}
export default App
memo 传递比较逻辑(使用 memo方法自定义比较逻辑,用于执行深层比较。)
import React, { memo, useEffect, useState } from "react";
function ShowName({ person }) {
console.log("showName render...");
return (
{person.name} 丨 {person.job}
);
}
function comparePerson(prevProps, nextProps) {
if (
prevProps.person.name !== nextProps.person.name ||
prevProps.person.age !== nextProps.person.age
) {
return false
}
return true
}
const ShowNameMemo = memo(ShowName, comparePerson);
function App() {
const [person, setPerson] = useState({ name: "张三", job: "developer" });
useEffect(() => {
setInterval(() => {
setPerson((data) => ({ ...data, name: "haoxuan" }));
}, 1000);
}, []);
return (
);
}
export default App;
使用组件懒加载
使用组件懒加载可以减少 bundle 文件大小, 加快组件呈递速度。
参考React实战视频讲解:进入学习
路由组件懒加载
import React, { lazy, Suspense } from "react"
import { BrowserRouter, Link, Route, Switch } from "react-router-dom"
const Home = lazy(() => import(/* webpackChunkName: "Home" */ "./Home"))
const List = lazy(() => import(/* webpackChunkName: "List" */ "./List"))
function App() {
return (
Home
List
Loading