您现在的位置是:网站首页> 编程资料编程资料
React 组件性能最佳优化实践分享_React_
2023-05-24
437人已围观
简介 React 组件性能最佳优化实践分享_React_
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 Appmemo 传递比较逻辑
(使用 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 文件大小, 加快组件呈递速度。
路由组件懒加载
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 ( HomeListLoading 根据条件进行组件懒加载(适用于组件不会随条件频繁切换)
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 ( Loading 使用Fragment 避免额外标记
为了满足这个条件我们通常都会在最外层添加一个div, 但是这样的话就会多出一个无意义的标记, 如果每个组件都多出这样的一个无意义标记的话, 浏览器渲染引擎的负担就会加剧。
import { Fragment } from "react" function App() { return ( message amessage b ) }function App() { return ( <>message amessage b> ) }不要使用内联函数定义
在使用内联函数后, render 方法每次运行时都会创建该函数的新实例, 导致 React 在进行 Virtual DOM 比对时, 新旧函数比对不相等,导致 React 总是为元素绑定新的函数实例, 而旧的函数实例又要交给垃圾回收器处理。
错误示范:
import React from "react" export default class App extends React.Component { constructor() { super() this.state = { inputValue: "" } } render() { return ( this.setState({ inputValue: e.target.value })} /> ) } }正确的做法是在组件中单独定义函数, 将函数绑定给事件:
import React from "react" export default class App extends React.Component { constructor() { super() this.state = { inputValue: "" } } setInputValue = e => { this.setState({ inputValue: e.target.value }) } render() { return ( ) } }在构造函数中进行函数this绑定
在类组件中如果使用 fn() {} 这种方式定义函数, 函数 this 默认指向 undefined. 也就是说函数内部的 this 指向需要被更正.
可以在构造函数中对函数的 this 进行更正, 也可以在行内进行更正, 两者看起来没有太大区别, 但是对性能的影响是不同的
export default class App extends React.Component { constructor() { super() // 方式一 // 构造函数只执行一次, 所以函数 this 指向更正的代码也只执行一次. this.handleClick = this.handleClick.bind(this) } handleClick() { console.log(this) } render() { // 方式二 // 问题: render 方法每次执行时都会调用 bind 方法生成新的函数实例. return } }类组件中的箭头函数
在类组件中使用箭头函数不会存在 this 指向问题, 因为箭头函数本身并不绑定 this。
export default class App extends React.Component { handleClick = () => console.log(this) render() { return } }箭头函数在 this 指向问题上占据优势, 但是同时也有不利的一面.
当使用箭头函数时, 该函数被添加为类的实例对象属性, 而不是原型对象属性. 如果组件被多次重用, 每个组件实例对象中都将会有一个相同的函数实例, 降低了函数实例的可重用性造成了资源浪费.
综上所述, 更正函数内部 this 指向的最佳做法仍是在构造函数中使用 bind 方法进行绑定
优化条件渲染
频繁的挂载和卸载组件是一项耗性能的操作, 为了确保应用程序的性能, 应该减少组件挂载和卸载的次数.
在 Rea
相关内容
- react使用websocket实时通信方式_React_
- vue如何设置动态的栅格占位、水平偏移量、类名、样式_vue.js_
- js 如何删除对象里的某个属性_javascript技巧_
- Vue mock.js模拟数据实现首页导航与左侧菜单功能_vue.js_
- Nodejs excel(.xlsx) 文件的读写方式_node.js_
- typescript中声明合并介绍_javascript技巧_
- 小程序展示弹窗常见API实例详解_javascript技巧_
- 使用react-native-image-viewer实现大图预览_React_
- JS if else语句(条件判断语句)的详细使用_javascript技巧_
- Vite+React搭建开发构建环境实践记录_React_
