React 性能优化
嘿伙计们,我希望你们都过得很好。
时隔许久,我又开始专注于在 dev.to 上撰写文章。在本文中,我想介绍一种避免 React 组件重新渲染的方法。
#1 避免向子组件传递不必要的 props
具有有限 props 的组件总是比具有大量 props 的组件性能更好。这始终是删除子组件中未使用的 props 的好方法。这里有一个清晰展示此示例的例子。
import React from 'react'
import { render } from 'react-dom'
function Avatar(props) {
return (
<div className="avatar-wrapper">
<img className="avatar-img" alt="avatar" src={props.user.image} />
<div className="avatar-name">{props.user.name}</div>
</div>
)
}
const user = {
id: 1,
name: 'Leanne Graham',
image: 'https://i.picsum.photos/id/237/200/300.jpg',
username: 'Bret',
email: 'Sincere@april.biz',
address: {
street: 'Kulas Light',
city: 'Gwenborough',
zipcode: '92998-3874',
},
}
render(<Avatar user={user} />, document.getElementById('root'))
在这个例子中,<Avatar />
组件只需要image
和name
属性。因此,每当其他属性(例如username
、email
或 )address
更新时,<Avatar />
组件都会重新渲染。从长远来看,这会导致性能问题。有很多方法可以删除属性,如何删除传递的属性完全取决于你。以下是我使用的方法。
import React from 'react'
import { render } from 'react-dom'
function Avatar(props) {
return (
<div className="avatar-wrapper">
<img className="avatar-img" alt="avatar" src={props.image} />
<div className="avatar-name">{props.name}</div>
</div>
)
}
const user = {
id: 1,
name: 'Leanne Graham',
image: 'https://i.picsum.photos/id/237/200/300.jpg',
username: 'Bret',
email: 'Sincere@april.biz',
address: {
street: 'Kulas Light',
city: 'Gwenborough',
zipcode: '92998-3874',
},
}
render(
<Avatar name={user.name} image={user.image} />,
document.getElementById('root')
)
#2 对象和函数 props 的常见修复场景
React
是沿着组件层级向下的单向数据流。因此,有时我们可能需要将函数传递给子组件。当我们将对象和函数式 props 传递给子组件时,我们需要执行额外的步骤,以避免在重新渲染期间重新创建对象和函数。这里有一个示例可以更好地解释这个概念。
import React from 'react'
import { render } from 'react-dom'
function Alert(props) {
return (
<div
className="alert-wrapper"
style={{ display: props.showAlert ? 'block' : 'none' }}
>
<div className="alert-close" onClick={props.handleCloseAlert}>
X
</div>
<div className="alert-title">{props.alertData.title}</div>
<div className="alert-description">{props.alertData.description}</div>
</div>
)
}
function App() {
const [showAlert, setShowAlert] = React.useState(false)
const [counter, setCounter] = React.useState(0)
const alertData = {
title: 'There was an error processing your request',
description: 'Please try again...',
}
const handleShowAlert = () => {
setShowAlert(true)
}
const handleCloseAlert = () => {
setShowAlert(false)
}
const handleIncrementCounter = () => {
setCounter(counter + 1)
}
return (
<div>
<button onClick={handleIncrementCounter}>counter: {counter}</button>
<button onClick={handleShowAlert}>Show Me Alert</button>
<Alert
showAlert={showAlert}
alertData={alertData}
handleCloseAlert={handleCloseAlert}
/>
</div>
)
}
render(<App />, document.getElementById('root'))
在这个例子中,我们创建了两个组件<App />
,其中一个是父组件,我们在其中定义了用于处理组件的状态逻辑。以下是一些分析器图像<Alert />
,可以帮助您了解具体情况。<App/>
<Alert />
ReactDevTool
每当父组件状态更新时,子组件也会重新渲染,我们可以通过使用 、 或 方法来避免子组件的重新渲染memo
。PureComponent
但这shouldComponentUpdate()
无法帮助您比较对象和函数式的 props,因为每次都会为对象和函数创建一个新的引用。有几种方法可以防止重新创建它。
对于对象,您需要React.useMemo()
像下面这样将对象包装在里面。
const alertData.= React.useMemo(() => {
title: 'There was an error processing your request',
description: 'Please try again...'
}, [])
对于函数,您需要React.useCallback()
像下面这样将函数包装在里面。
const handleCloseAlert = React.useCallback(() => {
setShowAlert(false)
}, [])
React.useMemo()
和的第二个参数React.useCallback()
是依赖项数组。一个空的依赖项数组,其中包含我们不想重新计算的值React.useMemo()
和记忆回调的特定依赖项React.useCallback()
。在某些情况下,我们可能需要重新计算值和记忆回调,这取决于你。
这是上述示例的改进版本。
import React from 'react'
import { render } from 'react-dom'
function Alert(props) {
return (
<div
className="alert-wrapper"
style={{ display: props.showAlert ? 'block' : 'none' }}
>
<div className="alert-close" onClick={props.handleCloseAlert}>
X
</div>
<div className="alert-title">{props.alertData.title}</div>
<div className="alert-description">{props.alertData.description}</div>
</div>
)
}
function App() {
const [showAlert, setShowAlert] = React.useState(false)
const [counter, setCounter] = React.useState(0)
const alertData = React.useMemo(
() => ({
title: 'There was an error processing your request',
description: 'Please try again...',
}),
[]
)
const handleShowAlert = React.useCallback(() => {
setShowAlert(true)
}, [])
const handleCloseAlert = React.useCallback(() => {
setShowAlert(false)
}, [])
const handleIncrementCounter = React.useCallback(() => {
setCounter(counter + 1)
}, [counter])
return (
<div>
<button onClick={handleIncrementCounter}>counter: {counter}</button>
<button onClick={handleShowAlert}>Show Me Alert</button>
<Alert
showAlert={showAlert}
alertData={alertData}
handleCloseAlert={handleCloseAlert}
/>
</div>
)
}
render(<App />, document.getElementById('root'))
#3 React.memo 与 react-fast-compare
对每个组件都使用缓存React.memo()
是有风险的,因为它会显式地缓存函数,这意味着它会将结果存储在内存中。如果对过多或过大的组件执行此操作,会导致更多的内存消耗。因此,在缓存大型组件时应格外小心。
为什么 React 不默认在每个组件周围都使用 memo() ?这样不是更快吗?我们应该做一个基准测试来检验一下吗?
问问自己:
为什么不在每个函数周围都使用 Lodash memoize() ?这样难道不会让所有函数都更快吗?我们需要为此做一个基准测试吗?为什么不呢?2019年1月12日 上午01:22
通常,我们可以通过传递有限的 props 来避免重新渲染。如果您仍然想使用,React.memo()
那么首先看看默认设置是否React.memo
适合您。如果不行,则使用分析器来了解并识别组件中的瓶颈。毕竟,您已经确定可以通过在和ReactDevTools
之间进行深度相等性检查来解决这个问题。prevProps
nextProps
让我们看看这个例子,
import React from 'react'
import { render } from 'react-dom'
import isEqual from 'react-fast-compare'
function Input(props) {
return <input value={props.value} onChange={props.handleOnChange} />
}
const MemoizedInput = React.memo(Input, isEqual)
function App() {
const [username, setUsername] = React.useState('')
const handleOnChange = React.useCallback((e) => {
setUsername(e.target.value)
}, [])
return <MemoizedInput value={username} handleOnChange={handleOnChange} />
}
render(<App />, document.getElementById('root'))
感谢阅读。希望你喜欢这篇文章,欢迎点赞、评论或分享给你的朋友。
文章来源:https://dev.to/sagar/things-should-be-considered-while-writing-your-react-components-3n4a