React?Hook?四種組件優(yōu)化總結
前言
React Hook 已成為當前最流行的開發(fā)范式,React 16.8 以后基于 Hook 開發(fā)極大簡化開發(fā)者效率,同時不正確的使用 React Hook也帶來了很多的性能問題,本文梳理基于 React Hook 開發(fā)組件的過程中如何提高性能。
組件抽取
優(yōu)化前
每次點擊 Increase
都會引起子組件 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>計數(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
抽取成獨立的組件即可。此時點擊按鈕,子組件并不會渲染。
/** ?*?優(yōu)化后,Increase提取以后,上下文發(fā)生變化,組件內 ?*?@returns? ?*/ function?Increase(){ ????console.log('Child?Increase') ????let?[count,setCount]?=?useState(0) ????const?handleClick?=?()=>{ ????????setCount(count+1) ????} ????return?( ????????<div> ????????????<div?className='l50'> ????????????????<label>計數(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)化后,當點擊按鈕后,也不會觸發(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>計數(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?方法的返回結果與 ??將?prevProps?傳入?render?方法的返回結果一致則返回?true, ??否則返回?false ??*/ } export?default?React.memo(TestComponent,areEqual)
與 class 組件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 會返回 true;如果 props 不相等,則返回 false。這與 shouldComponentUpdate 方法的返回值相反。
useCallback 優(yōu)化組件
如果已經(jīng)用了 memo
,當遇到下面這種場景時,同樣會觸發(fā)子組件渲染。比如,給 Child
綁定一個 handleClick
,子組件內部增加一個按鈕,當點擊子組件的按鈕時,更改 count
值,即使沒有發(fā)生 name
變化,也同樣會觸發(fā)子組件渲染,為什么?memo
不是會判斷 name
變化了,才會更新嗎?
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>計數(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
沒有生效,是因為當狀態(tài)發(fā)生變化時,父組件會從新執(zhí)行,導致從新創(chuàng)建了新的handleChange
函數(shù),而 handleChange
的變化導致了子組件的再次渲染。
優(yōu)化后
點擊父組件的Increase
按鈕,更改了 count
值,經(jīng)過 useCallback
包裹 handleChange
函數(shù)以后,我們會發(fā)現(xiàn)子組件不再渲染,說明每當父組件執(zhí)行的時候,并沒有創(chuàng)建新的 handleChange
函數(shù),這就是通過 useCallback
優(yōu)化后的效果。 即使我們點擊子組件的按鈕,也同樣不會觸發(fā)子組件的渲染,同樣 count
會進行累加。
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>計數(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(){ ??//?點擊按鈕調用此函數(shù),但返回被緩存 ??const?onClick?=?useCallback(()?=>?{ ????console.log('我被緩存了,怎么點擊都返回一樣'); ??},?[]); ??return?(? ????<button?onClick={onClick}>點擊</button> ??); }
useCallback
接收 2 個參數(shù),第一個為緩存的函數(shù),第二個為依賴值- 主要用于緩存函數(shù),第二次會返回同樣的結果。
useMemo 優(yōu)化
我們定義了一個total
函數(shù),內部使用 1 填充了100次,通過 reduce
計算總和,經(jīng)過測試發(fā)現(xiàn)點擊 Increase
按鈕后,只會執(zhí)行 total1
,不會執(zhí)行 total2
,假設total
計算量巨大,就會造成內存的浪費,通過 useMemo
可以幫我們緩存計算值。
function?Before(){ ????console.log('Demo1?Parent') ????let?[count,setCount]?=?useState(0) ????const?handleClick?=?()=>{ ????????setCount(count+1) ????} ????const?total1?=?()=>{ ????????console.log('計算求和1') ????????let?arr?=?Array.from({?length:100?}).fill(1) ????????return?arr.reduce((prev,next)=>prev+next,0) ????} ????//?緩存對象值 ????const?total2?=?useMemo(()=>{ ????????console.log('計算求和2') ????????let?arr?=?Array.from({?length:100?}).fill(1) ????????return?arr.reduce((prev,next)=>prev+next,0) ????},[count]) ????return?( ????????<div> ????????????<div?className='l50'> ????????????????<label>計數(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]);
- 傳入一個函數(shù)進去,會返回一個
memoized
值,需要注意的是,函數(shù)內必須有返回值 - 第二個參數(shù)會依賴值,當依賴值更新時,會從新計算。
useCallback 和 useMemo 區(qū)別
他們都用于緩存,useCallback
主要用于緩存函數(shù),返回一個 緩存后 函數(shù),而 useMemo
主要用于緩存值,返回一個緩存后的值。
到此這篇關于React Hook 四種組件優(yōu)化總結的文章就介紹到這了,更多相關React Hook 組件優(yōu)化內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解決React報錯`value` prop on `input` should&
這篇文章主要為大家介紹了React報錯`value` prop on `input` should not be null解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12React的createElement和render手寫實現(xiàn)示例
這篇文章主要為大家介紹了React的createElement和render手寫實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08