React性能優(yōu)化SCU
React更新機(jī)制
? React在props或state發(fā)生改變時(shí),會調(diào)用React的render方法,會創(chuàng)建一顆不同的樹。
? React需要基于這兩顆不同的樹之間的差別來判斷如何有效的更新UI:
? ? 如果一棵樹參考另外一棵樹進(jìn)行完全比較更新,那么即使是最先進(jìn)的算法,該算法的復(fù)雜程度為 O(n3),其中 n 是樹中元素的數(shù)量;
? ? https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf;
? ? 如果在 React 中使用了該算法,那么展示 1000 個元素所需要執(zhí)行的計(jì)算量將在十億的量級范圍;
? ? 這個開銷太過昂貴了,React的更新性能會變得非常低效;
? 于是,React對這個算法進(jìn)行了優(yōu)化,將其優(yōu)化成了O(n),如何優(yōu)化的呢?
? ? 同層節(jié)點(diǎn)之間相互比較,不會垮節(jié)點(diǎn)比較;
? ? 不同類型的節(jié)點(diǎn),產(chǎn)生不同的樹結(jié)構(gòu);
? ? 開發(fā)中,可以通過key來指定哪些節(jié)點(diǎn)在不同的渲染下保持穩(wěn)定;
keys的優(yōu)化
? 方式一:在最后位置插入數(shù)據(jù)
? ? 這種情況,有無key意義并不大
? 方式二:在前面插入數(shù)據(jù)
? ? 這種做法,在沒有key的情況下,所有的li都需要進(jìn)行修改;
? 當(dāng)子元素(這里的li)擁有 key 時(shí),React 使用 key 來匹配原有樹上的子元素以及最新樹上的子元素:
? ? 在下面這種場景下,key為111和222的元素僅僅進(jìn)行位移,不需要進(jìn)行任何的修改;
? ? 將key為333的元素插入到最前面的位置即可;
? key的注意事項(xiàng):
? ? key應(yīng)該是唯一的;
? ? key不要使用隨機(jī)數(shù)(隨機(jī)數(shù)在下一次render時(shí),會重新生成一個數(shù)字);
? ? 使用index作為key,對性能是沒有優(yōu)化的;
render函數(shù)被調(diào)用
? 是修改了App中的數(shù)據(jù),所有的組件都需要重新render,進(jìn)行diff算法,性能必然是很低的:
? ? 事實(shí)上,很多的組件沒有必須要重新render;
? ? 它們調(diào)用render應(yīng)該有一個前提,就是依賴的數(shù)據(jù)(state、props)發(fā)生改變時(shí),再調(diào)用自己的render方法;
? 如何來控制render方法是否被調(diào)用呢?
? ? 通過shouldComponentUpdate方法即可;
shouldComponentUpdate
? React給我們提供了一個生命周期方法 shouldComponentUpdate(很多時(shí)候,我們簡稱為SCU),這個方法接受參數(shù),并且需要有返回值:
? 該方法有兩個參數(shù):
? ? 參數(shù)一:nextProps 修改之后,最新的props屬性
? ? 參數(shù)二:nextState 修改之后,最新的state屬性
? 該方法返回值是一個boolean類型:
? ? 返回值為true,那么就需要調(diào)用render方法;
? ? 返回值為false,那么久不需要調(diào)用render方法;
? ? 默認(rèn)返回的是true,也就是只要state發(fā)生改變,就會調(diào)用render方法;
? 比如我們在App中增加一個message屬性:
? ? jsx中并沒有依賴這個message,那么它的改變不應(yīng)該引起重新渲染;
? ? 但是因?yàn)閞ender監(jiān)聽到state的改變,就會重新render,所以最后render方法還是被重新調(diào)用了;
PureComponent
? 如果所有的類,我們都需要手動來實(shí)現(xiàn) shouldComponentUpdate,那么會給我們開發(fā)者增加非常多的工作量。
? ? 我們來設(shè)想一下shouldComponentUpdate中的各種判斷的目的是什么?
? ? props或者state中的數(shù)據(jù)是否發(fā)生了改變,來決定shouldComponentUpdate返回true或者false;
? 事實(shí)上React已經(jīng)考慮到了這一點(diǎn),所以React已經(jīng)默認(rèn)幫我們實(shí)現(xiàn)好了,如何實(shí)現(xiàn)呢?
? ? 將class繼承自PureComponent。
shallowEqual方法
這個方法中,調(diào)用 !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState),這個shallowEqual就是進(jìn)行淺層比較:
高階組件memo
? 目前我們是針對類組件可以使用PureComponent,那么函數(shù)式組件呢?
? ? 事實(shí)上函數(shù)式組件我們在props沒有改變時(shí),也是不希望其重新渲染其DOM樹結(jié)構(gòu)的
? 我們需要使用一個高階組件memo:
獲取DOM方式refs
如何使用ref
? 在React的開發(fā)模式中,通常情況下不需要、也不建議直接操作DOM原生,但是某些特殊的情況,確實(shí)需要獲取到DOM進(jìn)行某些操作:
? ? 管理焦點(diǎn),文本選擇或媒體播放;
? ? 觸發(fā)強(qiáng)制動畫;
? ? 集成第三方 DOM 庫;
? ? 我們可以通過refs獲取DOM;
? 如何創(chuàng)建refs來獲取對應(yīng)的DOM呢?目前有三種方式:
? 方式一:傳入字符串
? ? 使用時(shí)通過 this.refs.傳入的字符串格式獲取對應(yīng)的元素;
? 方式二:傳入一個對象
? ? 對象是通過 React.createRef() 方式創(chuàng)建出來的;
? ? 使用時(shí)獲取到創(chuàng)建的對象其中有一個current屬性就是對應(yīng)的元素;
? 方式三:傳入一個函數(shù)
? ? 該函數(shù)會在DOM被掛載時(shí)進(jìn)行回調(diào),這個函數(shù)會傳入一個 元素對象,我們可以自己保存;
? ? 使用時(shí),直接拿到之前保存的元素對象即可;
ref的類型
? ref 的值根據(jù)節(jié)點(diǎn)的類型而有所不同:
? ? 當(dāng) ref 屬性用于 HTML 元素時(shí),構(gòu)造函數(shù)中使用 React.createRef() 創(chuàng)建的 ref 接收底層 DOM 元素作為其 current 屬性;
? ? 當(dāng) ref 屬性用于自定義 class 組件時(shí),ref 對象接收組件的掛載實(shí)例作為其 current 屬性;
? ? 你不能在函數(shù)組件上使用 ref 屬性,因?yàn)樗麄儧]有實(shí)例;
? 函數(shù)式組件是沒有實(shí)例的,所以無法通過ref獲取他們的實(shí)例:
? ? 但是某些時(shí)候,我們可能想要獲取函數(shù)式組件中的某個DOM元素;
? ? 這個時(shí)候我們可以通過 React.forwardRef ,后面我們也會學(xué)習(xí) hooks 中如何使用ref;
受控和非受控組件
認(rèn)識受控組件
? 在React中,HTML表單的處理方式和普通的DOM元素不太一樣:表單元素通常會保存在一些內(nèi)部的state。
? 比如下面的HTML表單元素:
? ? 這個處理方式是DOM默認(rèn)處理HTML表單的行為,在用戶點(diǎn)擊提交時(shí)會提交到某個服務(wù)器中,并且刷新頁面;
? ? 在React中,并沒有禁止這個行為,它依然是有效的;
? ? 但是通常情況下會使用JavaScript函數(shù)來方便的處理表單提交,同時(shí)還可以訪問用戶填寫的表單數(shù)據(jù);
? ? 實(shí)現(xiàn)這種效果的標(biāo)準(zhǔn)方式是使用“受控組件”;
? 在 HTML 中,表單元素(如、 和 )之類的表單元素通常自己維護(hù) state,并根據(jù)用戶輸入進(jìn)行更新。
? 而在 React 中,可變狀態(tài)(mutable state)通常保存在組件的 state 屬性中,并且只能通過使用 setState()來更新。
? ? 我們將兩者結(jié)合起來,使React的state成為“唯一數(shù)據(jù)源”;
? ? 渲染表單的 React 組件還控制著用戶輸入過程中表單發(fā)生的操作;
? ? 被 React 以這種方式控制取值的表單輸入元素就叫做“受控組件”;
? 由于在表單元素上設(shè)置了 value 屬性,因此顯示的值將始終為 this.state.value,這使得 React 的 state 成為唯一數(shù)據(jù)源。
? 由于 handleUsernameChange 在每次按鍵時(shí)都會執(zhí)行并更新 React 的 state,因此顯示的值將隨著用戶輸入而更新。
非受控組件
? React推薦大多數(shù)情況下使用 受控組件 來處理表單數(shù)據(jù):
? ? 一個受控組件中,表單數(shù)據(jù)是由 React 組件來管理的;
? ? 另一種替代方案是使用非受控組件,這時(shí)表單數(shù)據(jù)將交由 DOM 節(jié)點(diǎn)來處理;
? 如果要使用非受控組件中的數(shù)據(jù),那么我們需要使用 ref 來從DOM節(jié)點(diǎn)中獲取表單數(shù)據(jù)。
? ? 我們來進(jìn)行一個簡單的演練:
? ? 使用ref來獲取input元素;
? 在非受控組件中通常使用defaultValue來設(shè)置默認(rèn)值;
? 同樣, 和 支持 defaultChecked, 和 支持 defaultValue。
React的高階組件
認(rèn)識高階函數(shù)
? 高階函數(shù)的維基百科定義:至少滿足以下條件之一:
? ? 接受一個或多個函數(shù)作為輸入;
? ? 輸出一個函數(shù);
? JavaScript中比較常見的filter、map、reduce都是高階函數(shù)。
? 那么說明是高階組件呢?
? ? 高階組件的英文是 Higher-Order Components,簡稱為 HOC;
? ? 官方的定義:高階組件是參數(shù)為組件,返回值為新組件的函數(shù);
? 我們可以進(jìn)行如下的解析:
? ? 首先, 高階組件 本身不是一個組件,而是一個函數(shù);
? ? 其次,這個函數(shù)的參數(shù)是一個組件,返回值也是一個組件;
高階組件的定義
? 高階組件并不是React API的一部分,它是基于React的****組合特性而形成的設(shè)計(jì)模式;
? 高階組件在一些React第三方庫中非常常見:
? ? 比如redux中的connect;(后續(xù)會講到)
? ? 比如react-router中的withRouter;(后續(xù)會講到)
? 組件的名稱問題:
? ? 在ES6中,類表達(dá)式中類名是可以省略的;
? ? 組件的名稱都可以通過displayName來修改;
應(yīng)用一 – props的增強(qiáng)
? 不修改原有代碼的情況下,添加新的props
? 利用高階組件來共享Context
應(yīng)用二 – 渲染判斷鑒權(quán)
? 在開發(fā)中,我們可能遇到這樣的場景:
? ? 某些頁面是必須用戶登錄成功才能進(jìn)行進(jìn)入;
? ? 如果用戶沒有登錄成功,那么直接跳轉(zhuǎn)到登錄頁面;
? 這個時(shí)候,我們就可以使用高階組件來完成鑒權(quán)操作:
應(yīng)用三 – 生命周期劫持
? 我們也可以利用高階函數(shù)來劫持生命周期,在生命周期中完成自己的邏輯:
高階函數(shù)的意義
? 我們會發(fā)現(xiàn)利用高階組件可以針對某些React代碼進(jìn)行更加優(yōu)雅的處理。
? 其實(shí)早期的React有提供組件之間的一種復(fù)用方式是mixin,目前已經(jīng)不再建議使用:
? ? Mixin 可能會相互依賴,相互耦合,不利于代碼維護(hù);
? ? 不同的Mixin中的方法可能會相互沖突;
? ? Mixin非常多時(shí),組件處理起來會比較麻煩,甚至還要為其做相關(guān)處理,這樣會給代碼造成滾雪球式的復(fù)雜性;
? 當(dāng)然,HOC也有自己的一些缺陷:
? ? HOC需要在原組件上進(jìn)行包裹或者嵌套,如果大量使用HOC,將會產(chǎn)生非常多的嵌套,這讓調(diào)試變得非常困難;
? ? HOC可以劫持props,在不遵守約定的情況下也可能造成沖突;
? Hooks的出現(xiàn),是開創(chuàng)性的,它解決了很多React之前的存在的問題
? ? 比如this指向問題、比如hoc的嵌套復(fù)雜度問題等等;
ref的轉(zhuǎn)發(fā)
? 在前面我們學(xué)習(xí)ref時(shí)講過,ref不能應(yīng)用于函數(shù)式組件:
? ? 因?yàn)楹瘮?shù)式組件沒有實(shí)例,所以不能獲取到對應(yīng)的組件對象
? 但是,在開發(fā)中我們可能想要獲取函數(shù)式組件中某個元素的DOM,這個時(shí)候我們應(yīng)該如何操作呢?
? ? 方式一:直接傳入ref屬性(錯誤的做法)
? ? 方式二:通過forwardRef高階函數(shù);
portals和fragment
Portals的使用
? 某些情況下,我們希望渲染的內(nèi)容獨(dú)立于父組件,甚至是獨(dú)立于當(dāng)前掛載到的DOM元素中(默認(rèn)都是掛載到id為root的DOM元素上的)。
? Portal 提供了一種將子節(jié)點(diǎn)渲染到存在于父組件以外的 DOM 節(jié)點(diǎn)的優(yōu)秀的方案:
? ? 第一個參數(shù)(child)是任何可渲染的 React 子元素,例如一個元素,字符串或 fragment;
? ? 第二個參數(shù)(container)是一個 DOM 元素;
? 通常來講,當(dāng)你從組件的 render 方法返回一個元素時(shí),該元素將被掛載到 DOM 節(jié)點(diǎn)中離其最近的父節(jié)點(diǎn):
? 然而,有時(shí)候?qū)⒆釉夭迦氲?DOM 節(jié)點(diǎn)中的不同位置也是有好處的:
fragment
? 在之前的開發(fā)中,我們總是在一個組件中返回內(nèi)容時(shí)包裹一個div元素
? 我們又希望可以不渲染這樣一個div應(yīng)該如何操作呢?
? ? 使用Fragment
? ? Fragment 允許你將子列表分組,而無需向 DOM 添加額外節(jié)點(diǎn);
? React還提供了Fragment的短語法:
? ? 它看起來像空標(biāo)簽 <> </>;
? ? 但是,如果我們需要在Fragment中添加key,那么就不能使用短語法
StrictMode嚴(yán)格模式
? StrictMode 是一個用來突出顯示應(yīng)用程序中潛在問題的工具:
? ? 與 Fragment 一樣,StrictMode 不會渲染任何可見的 UI;
? ? 它為其后代元素觸發(fā)額外的檢查和警告;
? ? 嚴(yán)格模式檢查僅在開發(fā)模式下運(yùn)行;它們不會影響生產(chǎn)構(gòu)建;
? 可以為應(yīng)用程序的任何部分啟用嚴(yán)格模式:
? 不會對 Header 和 Footer 組件運(yùn)行嚴(yán)格模式檢查;
? 但是,ComponentOne 和 ComponentTwo 以及它們的所有后代元素都將進(jìn)行檢查;
嚴(yán)格模式檢查的是什么?
? 1.識別不安全的生命周期:
? 2.使用過時(shí)的ref API
? 3.檢查意外的副作用
? ? 這個組件的constructor會被調(diào)用兩次;
? ? 這是嚴(yán)格模式下故意進(jìn)行的操作,讓你來查看在這里寫的一些邏輯代碼被調(diào)用多次時(shí),是否會產(chǎn)生一些副作用;
? ? 在生產(chǎn)環(huán)境中,是不會被調(diào)用兩次的;
? 4.使用廢棄的findDOMNode方法
? ? 在之前的React API中,可以通過findDOMNode來獲取DOM,不過已經(jīng)不推薦使用了,可以自行學(xué)習(xí)演練一下
? 5.檢測過時(shí)的context API
? ? 早期的Context是通過static屬性聲明Context對象屬性,通過getChildContext返回Context對象等方式來使用Context的;文章來源:http://www.zghlxwxcb.cn/news/detail-817468.html
? ? 目前這種方式已經(jīng)不推薦使用,大家可以自行學(xué)習(xí)了解一下它的用法;文章來源地址http://www.zghlxwxcb.cn/news/detail-817468.html
到了這里,關(guān)于【React】組件性能優(yōu)化、高階組件的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!