事情发生在一个下午,我需要用React hooks写一个定时器,因为useEffect每次执行都会使组件重新渲染一次,我发现如果在根组件使用定时器的话,根组件里所有的子组件也都会跟着渲染一次,哪怕它和变化的数据没有任何关系,也会被渲染。
这个时候我就在想。这样如果有大量的组件在频繁的做无意义的渲染,不是会大大增加浏览器的负担吗?我们能不能让不必要的组件不用渲染呢?
带着这个问题,我开始探索react中的优化…
初始化渲染
在初始化渲染时,我们需要渲染整个应用
(绿色 = 已渲染节点)
我们想更新一部分数据。这些改变只和一个叶子节点相关.理想状态下是这样的.我们只想渲染通向叶子节点的关键路径上的这几个节点
(橘黄色 = 浪费的渲染)
由此可以看出,当我们每次渲染时,有些没有依赖值的组件也会造成渲染。
所以这就好造成浪费,下面让我们来看看怎么解决此类问题把!
根组件:
import React,{useState,useEffect} from 'react'
import Test1 from './Test1'
import Test2 from './Test2'
let timer = null
const App = () => {
const [count,setCount] = useState(12)
useEffect(() => {
timer = setInterval(() => {
setCount(n => {
if (n) { // 3.1 倒计时每秒减少1
return n - 1
} else { // 3.2 倒计时为0时,清空倒计时
clearInterval(timer)
return 0
}
});
}, 1000)
// 4 组件销毁 清楚倒计时
return ()=>{
clearInterval(timer)
}
}, [])
const [num,setNum] = useState(1)
return (
<>
<Test1 count={count}/>
<Test2 onClick={() => setNum(2)} num={num}/>
</>
)
}
export default App
子组件Test1:
import React from 'react'
const test1 = (props) => {
const {count} = props
console.log('test1',count);
return (
<div>
</div>
)
}
export default test1
子组件Test2:
import React from 'react'
const test2 = (props) => {
const {num} = props
console.log('test2',num);
return (
<div>
</div>
)
}
export default test2
可以看到我们的子组件随着count
变化会被不停的渲染,下面让我们来看看怎么让那些无关的吃瓜组件不凑热闹一起渲染!
被memo
包裹的组件在渲染时会对props做一次浅层次的对比,防止子组件出现额外的渲染
如果props没发生变化,就不会渲染
我们给Test2用memo包裹:
import React,{memo} from 'react'
const test2 = (props) => {
const {num} = props
console.log('test2',num);
return (
<div>
</div>
)
}
export default memo(test2)
注意!!
根组件给被包裹的子组件添加事件,会导致memo失效,它依旧会继续渲染
使用useMemo
缓存一个组件,相对于memo
的优点:
让我们来看看怎样使用吧!
根组件:
import React,{useState,useEffect,useMemo} from 'react'
import Test1 from './Test1'
import Test2 from './Test2'
let timer = null
const App = () => {
const [count,setCount] = useState(12)
useEffect(() => {
timer = setInterval(() => {
setCount(n => {
if (n) { // 3.1 倒计时每秒减少1
return n - 1
} else { // 3.2 倒计时为0时,清空倒计时
clearInterval(timer)
return 0
}
});
}, 1000)
// 4 组件销毁 清楚倒计时
return ()=>{
clearInterval(timer)
}
}, [])
const [num,setNum] = useState(1)
const NewTest2 = useMemo(() => <Test2 onClick={() => setNum(2)} num={num}/>, [num])
return (
<>
<Test1 count={count}/>
{NewTest2}
</>
)
}
export default App
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
我们上面写法也能使用useCallback实现
import React,{useState,useEffect,useCallback} from 'react'
import Test1 from './Test1'
import Test2 from './Test2'
let timer = null
const App = () => {
const [count,setCount] = useState(12)
useEffect(() => {
timer = setInterval(() => {
setCount(n => {
if (n) { // 3.1 倒计时每秒减少1
return n - 1
} else { // 3.2 倒计时为0时,清空倒计时
clearInterval(timer)
return 0
}
});
}, 1000)
// 4 组件销毁 清楚倒计时
return ()=>{
clearInterval(timer)
}
}, [])
const [num,setNum] = useState(1)
// const NewTest2 = useMemo(() => setNum(2)} num={num}/>, [num])
const NewTest2 = useCallback(<Test2 onClick={() => setNum(2)} num={num}/>,[num])
return (
<>
<Test1 count={count}/>
{NewTest2}
</>
)
}
export default App
react的优化手段还有很多,今天我们只是简单介绍了利用memo、useMemo和useCallback去减少重复渲染。
不积小流,无以成江海。