日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

React?Hook?四種組件優(yōu)化總結(jié)_React

作者:??河畔一角? ? 更新時(shí)間: 2022-09-19 編程語言

前言

React Hook 已成為當(dāng)前最流行的開發(fā)范式,React 16.8 以后基于 Hook 開發(fā)極大簡化開發(fā)者效率,同時(shí)不正確的使用 React Hook也帶來了很多的性能問題,本文梳理基于 React Hook 開發(fā)組件的過程中如何提高性能。

組件抽取

優(yōu)化前

每次點(diǎn)擊?Increase?都會(huì)引起子組件?Child?的渲染,哪怕子組件并沒有狀態(tài)變化

function?Before(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????let?[name,setName]?=?useState('-')
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計(jì)數(shù)器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?onChange={handleInput}/>
????????????</div>
????????????<hr?/>
????????????<Child?name={name}/>
????????</div>
????)
}
//?子組件
function?Child(props){
????console.log('Demo1?Child')
????return?(
????????<div?className='l50'>
????????????子組件渲染:{props.name}
????????</div>
????)
}

優(yōu)化后

只需要把?Increase?抽取成獨(dú)立的組件即可。此時(shí)點(diǎn)擊按鈕,子組件并不會(huì)渲染。

/**
?*?優(yōu)化后,Increase提取以后,上下文發(fā)生變化,組件內(nèi)
?*?@returns?
?*/
function?Increase(){
????console.log('Child?Increase')
????let?[count,setCount]?=?useState(0)
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計(jì)數(shù)器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????</div>
????)
}
function?After(){
????console.log('Demo1?Parent')
????let?[name,setName]?=?useState('-')
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????return?(
????????<div>
????????????<Increase/>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?onChange={handleInput}/>
????????????</div>
????????????<Child?name={name}/>
????????</div>
????)
}
//?子組件
function?Child(props){
????console.log('Demo1?Child')
????return?(
????????<div?className='l50'>
????????????子組件渲染:{props.name}
????????</div>
????)
}

memo 優(yōu)化組件

同樣基于上述優(yōu)化前代碼,如果不抽取組件,使用?memo?優(yōu)化后,當(dāng)點(diǎn)擊按鈕后,也不會(huì)觸發(fā)二次渲染。

//?優(yōu)化前
function?AfterMemo(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????let?[name,setName]?=?useState('-')
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計(jì)數(shù)器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?onChange={handleInput}/>
????????????</div>
????????????<Child?name={name}/>
????????</div>
????)
}

//?子組件
const?Child?=?memo((props)=>{
????console.log('Demo1?Child')
????return?(
????????<div?className='l50'>
????????????子組件渲染:{props.name}
????????</div>
????)
})

React.memo 語法

React.memo 為高階組件,與 React.PureComponent相似。

function?TestComponent(props){
??//?使用?props?渲染
}
function?areEqual(prevProps,nextProps){
??/*
??如果把?nextProps?傳入?render?方法的返回結(jié)果與
??將?prevProps?傳入?render?方法的返回結(jié)果一致則返回?true,
??否則返回?false
??*/
}
export?default?React.memo(TestComponent,areEqual)

與 class 組件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 會(huì)返回 true;如果 props 不相等,則返回 false。這與 shouldComponentUpdate 方法的返回值相反。

useCallback 優(yōu)化組件

如果已經(jīng)用了?memo?,當(dāng)遇到下面這種場景時(shí),同樣會(huì)觸發(fā)子組件渲染。比如,給?Child?綁定一個(gè)?handleClick?,子組件內(nèi)部增加一個(gè)按鈕,當(dāng)點(diǎn)擊子組件的按鈕時(shí),更改?count?值,即使沒有發(fā)生?name?變化,也同樣會(huì)觸發(fā)子組件渲染,為什么?memo?不是會(huì)判斷?name?變化了,才會(huì)更新嗎?

function?Before(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????let?[name,setName]?=?useState('-')
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????const?handleChange?=?()=>{
????????setCount(count+1)
????}
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計(jì)數(shù)器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?onChange={handleInput}/>
????????????</div>
????????????<Child?name={name}?handleClick={handleChange}/>
????????</div>
????)
}

//?子組件
const?Child?=?memo((props)=>{
????console.log('Demo1?Child')
????return?(
????????<div?className='l50'>
????????????子組件渲染:{props.name}
????????????<button?onClick={props.handleClick}>更改count</button>
????????</div>
????)
})

并不是?memo?沒有生效,是因?yàn)楫?dāng)狀態(tài)發(fā)生變化時(shí),父組件會(huì)從新執(zhí)行,導(dǎo)致從新創(chuàng)建了新的handleChange?函數(shù),而?handleChange?的變化導(dǎo)致了子組件的再次渲染。

優(yōu)化后

點(diǎn)擊父組件的Increase按鈕,更改了?count?值,經(jīng)過?useCallback?包裹?handleChange?函數(shù)以后,我們會(huì)發(fā)現(xiàn)子組件不再渲染,說明每當(dāng)父組件執(zhí)行的時(shí)候,并沒有創(chuàng)建新的?handleChange?函數(shù),這就是通過?useCallback?優(yōu)化后的效果。 即使我們點(diǎn)擊子組件的按鈕,也同樣不會(huì)觸發(fā)子組件的渲染,同樣?count?會(huì)進(jìn)行累加。

function?After(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????let?text?=?useRef();
????let?[name,setName]?=?useState('-')
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????const?handleChange?=?useCallback(()=>{
????????//?為了讓?count?能夠累加,我們使用ref?獲取值
????????let?val?=?parseInt(text.current.textContent);
????????setCount(val+1)
????},[])
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計(jì)數(shù)器:</label>
????????????????<span?className='mr10'?ref={text}>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?value={name}?onChange={handleInput}/>
????????????</div>
????????????<Child?name={name}?handleClick={handleChange}/>
????????</div>
????)
}

useCallback 作用

//?用法
useCallback(()=>{
??//?to-do
},[])

//?示例
function?App(){
??//?點(diǎn)擊按鈕調(diào)用此函數(shù),但返回被緩存
??const?onClick?=?useCallback(()?=>?{
????console.log('我被緩存了,怎么點(diǎn)擊都返回一樣');
??},?[]);

??return?(?
????<button?onClick={onClick}>點(diǎn)擊</button>
??);
}
  • useCallback?接收 2 個(gè)參數(shù),第一個(gè)為緩存的函數(shù),第二個(gè)為依賴值
  • 主要用于緩存函數(shù),第二次會(huì)返回同樣的結(jié)果。

useMemo 優(yōu)化

我們定義了一個(gè)total函數(shù),內(nèi)部使用 1 填充了100次,通過?reduce?計(jì)算總和,經(jīng)過測試發(fā)現(xiàn)點(diǎn)擊?Increase按鈕后,只會(huì)執(zhí)行?total1?,不會(huì)執(zhí)行?total2,假設(shè)total計(jì)算量巨大,就會(huì)造成內(nèi)存的浪費(fèi),通過?useMemo?可以幫我們緩存計(jì)算值。

function?Before(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?total1?=?()=>{
????????console.log('計(jì)算求和1')
????????let?arr?=?Array.from({?length:100?}).fill(1)
????????return?arr.reduce((prev,next)=>prev+next,0)
????}
????//?緩存對(duì)象值
????const?total2?=?useMemo(()=>{
????????console.log('計(jì)算求和2')
????????let?arr?=?Array.from({?length:100?}).fill(1)
????????return?arr.reduce((prev,next)=>prev+next,0)
????},[count])
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計(jì)數(shù)器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div>
????????????????<label>總和:</label>
????????????????<span>{total1()}</span>
????????????????<span>{total2}</span>
????????????</div>
????????</div>
????)
}

useMemo 語法

const?memoizedValue?=?useMemo(()?=>?computeExpensiveValue(a,?b),?[a,?b]);
  • 傳入一個(gè)函數(shù)進(jìn)去,會(huì)返回一個(gè)?memoized?值,需要注意的是,函數(shù)內(nèi)必須有返回值
  • 第二個(gè)參數(shù)會(huì)依賴值,當(dāng)依賴值更新時(shí),會(huì)從新計(jì)算。

useCallback 和 useMemo 區(qū)別

他們都用于緩存,useCallback?主要用于緩存函數(shù),返回一個(gè) 緩存后 函數(shù),而?useMemo?主要用于緩存值,返回一個(gè)緩存后的值。

原文鏈接:https://juejin.cn/post/7124338348885278757

欄目分類
最近更新