React 组件性能优化的核心是减少渲染真实 DOM 节点的频率,减少 Virtual DOM 比对的频率。
1. 组件卸载前进行清理操作
window 注册的全局事件, 以及定时器
useEffect(()=>{
return ()=>{
// do something
clearTimeout(tiemr)
window.removeEventListener('xxx', calback)
}
},[])
2. PureComponent
- 什么是纯组件
纯组件会对组件输入数据进行浅层比较,如果当前输入数据和上次输入数据相同,组件不会重新渲染。 - 什么是浅层比较
比较引用数据类型在内存中的引用地址是否相同,比较基本数据类型的值是否相同。 - 如何实现纯组件
类组件继承 PureComponent 类,函数组件使用 memo 方法 - 为什么不直接进行 diff 操作, 而是要先进行浅层比较,浅层比较难道没有性能消耗吗?
和进行 diff 比较操作相比,浅层比较将消耗更少的性能。diff 操作会重新遍历整颗 virtualDOM 树, 而浅层比较只操作当前组件的 state 和 props。
3. shouldComponentUpdate
纯组件只能进行浅层比较,要进行深层比较,使用 shouldComponentUpdate,它用于编写自定义比较逻辑。
返回 true 重新渲染组件,返回 false 阻止重新渲染。
函数的第一个参数为 nextProps, 第二个参数为 nextState.
shouldComponentUpdate(nextProps, nextState) {
if (this.state.name !== nextState.name || this.state.age !== nextState.age) {
return true
}
return false
}
4. React.memo
为 memo 传递比较逻辑
使用 memo方法自定义比较逻辑,用于执行深层比较。
比较函数的第一个参数为上一次的 props, 比较函数的第二个参数为下一次的 props, 比较函数返回 true, 不进行渲染, 比较函数返回 false, 组件重新渲染.
const ShowPersonMemo = memo(ShowPerson, comparePerson)
function comparePerson(prevProps, nextProps) {
if (
prevProps.person.name !== nextProps.person.name ||
prevProps.person.age !== nextProps.person.age
) {
return false
}
return true
}
5. 使用组件懒加载
使用组件懒加载可以减少 bundle 文件大小, 加快组件呈递速度.
- 路由组件懒加载
import React, { lazy, Suspense } from "react"
import { BrowserRouter, Link, Route, Switch } from "react-router-dom"
// 打包时生成对应名字的chunk
const Home = lazy(() => import(/* webpackChunkName: "Home" */ "./Home"))
const List = lazy(() => import(/* webpackChunkName: "List" */ "./List"))
function App() {
return (
<BrowserRouter>
<Link to="/">Home</Link>
<Link to="/list">List</Link>
<Switch>
// 组件未加载完成展示loading
<Suspense fallback={<div>Loading</div>}>
<Route path="/" component={Home} exact />
<Route path="/list" component={List} />
</Suspense>
</Switch>
</BrowserRouter>
)
}
export default App
根据条件进行组件懒加载
适用于组件不会随条件频繁切换
import React, { lazy, Suspense } from "react" function App() { let LazyComponent = null if (true) { LazyComponent = lazy(() => import(/* webpackChunkName: "Home" */ "./Home")) } else { LazyComponent = lazy(() => import(/* webpackChunkName: "List" */ "./List")) } return ( <Suspense fallback={<div>Loading</div>}> <LazyComponent /> </Suspense> ) } export default App
6. 使用 Fragment 避免额外标记
7. 不要使用内联函数定义
8. 在构造函数中进行函数this绑定
9. 类组件中的箭头函数
10. 避免使用内联样式属性
11. 优化条件渲染
12. 为组件创建错误边界
错误边界涉及到两个生命周期函数, 分别为 getDerivedStateFromError 和 componentDidCatch.
– getDerivedStateFromError 为静态方法, 方法中需要返回一个对象, 该对象会和state对象进行合并, 用于更改应用程序状态.
– componentDidCatch 方法用于记录应用程序错误信息. 该方法的参数就是错误对象.
ErrorBoundaries.js
import React from "react"
import App from "./App"
export default class ErrorBoundaries extends React.Component {
constructor() {
super()
this.state = {
hasError: false
}
}
componentDidCatch(error) {
console.log("componentDidCatch")
}
static getDerivedStateFromError() {
console.log("getDerivedStateFromError")
return {
hasError: true
}
}
render() {
if (this.state.hasError) {
return <div>发生了错误</div>
}
return <App />
}
}
App.js
import React from "react"
export default class App extends React.Component {
render() {
// throw new Error("lalala")
return <div>App works</div>
}
}
index.js
import React from "react"
import ReactDOM from "react-dom"
import ErrorBoundaries from "./ErrorBoundaries"
ReactDOM.render(<ErrorBoundaries />, document.getElementById("root"))
注意: 错误边界不能捕获异步错误, 比如点击按钮时发生的错误.
13. 避免数据结构突变
14. 依赖优化
主要是对包进行优化
参考:重构之路:webpack打包体积优化(超详细)