React 组件性能优化的核心是减少渲染真实DOM节点的频率,减少 VirtualDOM 比对的频率。
1.组件卸载前进行清理操作
在组件中为 window 注册的全局事件以及定时器,在组件卸载前要清理,防止组件卸载后继续执行,从而影响应用性能。
需求:开启定时器后卸载组件,查看组件中的定时器是否还在执行。
import React, {useState} from 'react';
import Test from "./Test";
function App() {
const [status, setStatus] = useState(true);
return (
{status&& }
);
}
export default App;
---------------------------------
import {useEffect} from "react";
function Test() {
const windowBindClick = () => {
console.log('windowBindClick')
}
useEffect(() => {
let timer= setInterval(() => {
console.log('定时器在执行')
}, 1000);
window.addEventListener('click', windowBindClick);
return () => {
clearInterval(timer);
window.removeEventListener('click', windowBindClick);
}
}, []);
return Test
}
export default Test;
2.PureComponent 通过纯组件提升组件性能
- 什么是纯组件
纯组件会对组件输入数据进行浅层比较,如果当前输入数据和上次输入数据相同,组件不会重新渲染。 - 什么是浅层比较
比较引用数据类型在内存中的地址是否相同,比较基本数据类型的值是否相同。 - 如何实现纯组件
类组件继承 PureComponent 类,函数组件使用memo方法。 - 为什么不直接进行diff操作,而是要先进行浅层比较,浅层比较难道没有消耗性能吗?
和进行diff比较操作相比,浅层比较将消耗更少的性能。diff操作会重新遍历整棵virtualDOM树,而浅层比较只操作当前组件的state和props。 需求:在状态对象中存储 name 值为张三,组件卸载完成后将 name 属性的值再次更改为张三,然后分别将 name 传递给纯组件和分纯组件,查看结果。
// 父组件 import React, {Component} from 'react'; import Impurity from "./impurity"; import Pure from "./pure"; class App extends Component{ constructor(props) { super(props); this.state = { name: '张三' } } handleSetName () { setInterval(() => { this.setState({ name: '张三' }) }, 1000) } render() { const {name} = this.state; return (
{this.props.name}) } } export default Pure; // 非纯子组件 import React, {Component} from 'react'; class Impurity extends Component { constructor(props) { super(props); } render () { console.log('Impurity render') return ({this.props.name}) } } export default Impurity;父组件每更新name属性为相同值,非纯组件都会重新渲染。
3.通过 shouldComponentUpdate 声明周期函数提升组件性能
纯组件只能进行浅层比较,要进行深层比较,使用 shouldComponentUpdate, 它用于编写自定义比较逻辑。
返回 true 重新渲染组件,返回 false 组织重新渲染。
函数的第一个参数为 nextProps,第二个参数为 nextState
需求: 在页面中展示员工信息,员工信息包括:姓名、年龄、职位,但在页面中只展示姓名和年龄,也就是说只有姓名和年龄发生变化时,才有必要重新渲染组件,如何员工的其他信息发生了变化就没必要重新渲染组件。
import React, {Component} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
name: '张三',
age: 18,
job: 'waiter',
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
...this.state,
job: 'developers'
})
}, 2000)
}
// 返回 fasle 使组件重新渲染
shouldComponentUpdate(nextProps, nextState, nextContext) {
return !this.state.name === nextState.name && this.state.age === nextState.age;
}
render () {
console.log('render...')
const {name, age} = this.state;
return
- {name}
- {age}
}
}
export default App;
4. React.memo
1. Memo 基本使用
将函数组件变为纯组件,将当前props和上一次的props进行浅层比较,如果相同就阻止组件重新渲染。
需求:父组件维护两个状态,index和name,开启定时器让index不断变化,name传递给子组件,查看父组件更新的同时,子组件是否也更新了。
import React, {memo, useEffect, useState} from 'react';
function App () {
const [index, setIndex] = useState(0);
const [name, setName] = useState('张三');
useEffect(() => {
const timer = setInterval(() => {
setIndex(index => index + 1);
console.log(index)
},1000)
return () => {
clearInterval(timer);
}
})
return(
)
}
// 父组件的 Index 每改变一次,子组件都会重新渲染
// function Children(props) {
// console.log('render...');
// return
// props.name
//
// }
// 使用memo优化子组件
const Children = memo((props) => {
return (
{props.name}
)
})
export default App;
2.为memo方法传递自定义比较逻辑
Memo 方法默认是浅层比较,如果将一个对象传递给子组件,并更新了对象中不必要的内容,子组件还是会重新渲染,如果要达到更新不必要内容不渲染的话,需要配置 memo 方法的第二个参数,第二个参数是一个函数,在函数中可以自定义比较逻辑,函数返回true是不更新返回false是更新,这一点与shouldComponentUpdate恰巧相反。
import React, {memo, useEffect, useState} from 'react';
function App () {
const [person, setPerson] = useState({
name: '张三',
age: 18,
job: 'waiter'
});
useEffect(() => {
const timer = setInterval(() => {
setPerson(person => ({...person, job: 'java开发'}));
},1000)
return () => {
clearInterval(timer);
}
}, []);
return(
)
}
const compare = (currProps, nextProps) => {
return currProps.name === nextProps.name && currProps.age === nextProps.age;
}
const Children = memo((props) => {
console.log('render...');
return (
{props.person.name}
{props.person.age}
)
}, compare)
export default App;
5.通过组件懒加载提升组件性能
如果不使用组件懒加载,那么所有代码都会打包进bundle文件中,那么初次加载会变的缓慢。如果是用组件懒加载,那么不同的组件会打包进不同的文件当中,可以有效的减少bundle文件体积,从而可以让初次加载速度变得更快。
1.路由组件懒加载
import React, {lazy, Suspense} from 'react';
import {BrowserRouter, Link, Route, Routes} from 'react-router-dom'; // v6
// 使用lazy方法懒加载组件
// /* webpackChunkName: "Home" */ 可指定打包文件名称(home.chunk.js)
const Home = lazy(() => import(/* webpackChunkName: "home" */'./Home'));
const List = lazy(() => import(/* webpackChunkName: "list" */'./List'));
function App () {
return(
// Suspense 指定内容未加载完成时要显示的内容,可以指定组件
loading...