提到 React 的性能优化,大家的脑海中应该会呈现 useMemo、Memo、useCallBack 这些词。这些都是官网提供的性能优化 hooks,的确也解决了咱们日常工作中遇到的大部分性能优化问题。
在看了某网红 (Dan Abramov) 的文章后,我想和大家分享两种不应用这些 hooks 解决性能问题的形式。
让咱们先来看个例子:
import {useState}from 'react';
const TimeMashine = ()=>{const now = Date.now();
console.log('111')
return (<p>I am time mashine</p>)
}
const App = ()=>{const [count,setCount]=useState(0);
const onClick=()=>{
const newCount=count+1;
setCount(newCount)
}
return (<div>
<button onClick={onClick}>click me</button>
<p>I clicked button {count} times</p>
<TimeMashine/>
</div>)
}
export default App
这个组件有着重大的性能问题,其中 TimeMashine 组件在按钮被点击的时候都会反复渲染。除了官网提供的 Memo 等优化形式,这里有还有两种形式能够做优化。
一、state 地位调整
const App = ()=>{const [count,setCount]=useState(0);
const onClick=()=>{
const newCount=count+1;
setCount(newCount)
}
return (<div>
<button onClick={onClick}>click me</button>
<p>I clicked button {count} times</p>
<TimeMashine/>
</div>)
}
认真看这段代码,咱们会发现这个按钮点击只是更新 p 标签中的文本内容,TimeMashie组件其实并不关怀点击事件。
import {useState}from 'react';
const TimeMashine = ()=>{const now = Date.now();
console.log('I am updated')
return (<p>I am time mashine</p>)
}
const Text = ()=>{const [count,setCount]=useState(0);
const onClick=()=>{
const newCount=count+1;
setCount(newCount)
}
return (<>
<button onClick={onClick}>click me</button>
<p>I clicked button {count} times</p>
</>)
}
const App = ()=>{
return (<div>
<Text/>
<TimeMashine/>
</div>)
}
export default App
二、内容晋升
让我来看另外一种状况
import {useState}from 'react';
const TimeMashine = ()=>{const now = Date.now();
console.log('I am updated')
return (<p>I am time mashine</p>)
}
const App = ()=>{const [count,setCount]=useState(0);
const onClick=()=>{
const newCount=count+1;
setCount(newCount)
}
return (<div>
<p>I clicked button {count} times</p>
<div>
<button onClick={onClick}>click me</button>
<TimeMashine/>
</div>
</div>)
}
export default App
能够看到 I clicked button {count} times
被晋升到了里面,显然不能再用形式一的方法来解决。
import {useState}from 'react';
const TimeMashine = ()=>{const now = Date.now();
console.log('I am updated')
return (<p>I am time mashine</p>)
}
const TimeAndText = ({children})=>{const [count,setCount]=useState(0);
const onClick=()=>{
const newCount=count+1;
setCount(newCount)
}
return (<>
<p>I clicked button {count} times</p>
<div>
<button onClick={onClick}>click me</button>
{children}
</div>
</>)
}
const App = ()=>{
return (
<TimeAndText>
<TimeMashine/>
</TimeAndText>)
}
export default App
咱们把按钮文本提取为独立的组件,TimMashine 作为依赖传入 TimeAndtext 组件。当点击按钮的时候,TimeAndtext会更新,然而 children 作为依赖没有变动所以不会更新。
Dan 认为这种模式自身并不是为了进步性能而写的,这种模式的目标是为了缩小组件的 props 参数数量而产生,然而意外的解决了性能上的问题。这个问题的确值得咱们思考。
参考
- Before You memo — Dan Abramov