摘要:并且處理特殊屬性,比如事件綁定。之后根據(jù)差異對(duì)象操作元素位置變動(dòng),刪除,添加等。當(dāng)節(jié)點(diǎn)數(shù)過(guò)大或者頁(yè)面更新次數(shù)過(guò)多時(shí),頁(yè)面卡頓的現(xiàn)象會(huì)比較明顯。基于注意使用來(lái)減少組件不必要的更新。
1、什么是Diff算法
傳統(tǒng)Diff:diff算法即差異查找算法;對(duì)于Html DOM結(jié)構(gòu)即為tree的差異查找算法;而對(duì)于計(jì)算兩顆樹(shù)的差異時(shí)間復(fù)雜度為O(n^3),顯然成本太高,React不可能采用這種傳統(tǒng)算法;
React Diff:
之前說(shuō)過(guò),React采用虛擬DOM技術(shù)實(shí)現(xiàn)對(duì)真實(shí)DOM的映射,即React Diff算法的差異查找實(shí)質(zhì)是對(duì)兩個(gè)JavaScript對(duì)象的差異查找;
基于三個(gè)策略:
Web UI 中 DOM 節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作特別少,可以忽略不計(jì)。(tree diff)
擁有相同類的兩個(gè)組件將會(huì)生成相似的樹(shù)形結(jié)構(gòu),擁有不同類的兩個(gè)組件將會(huì)生成不同的樹(shù)形結(jié)(component diff)
對(duì)于同一層級(jí)的一組子節(jié)點(diǎn),它們可以通過(guò)唯一 id 進(jìn)行區(qū)分。(element diff)
2、React Diff算法解讀首先需要明確,只有在React更新階段才會(huì)有Diff算法的運(yùn)用;
React更新機(jī)制:
React Diff算法優(yōu)化策略圖:
React更新階段會(huì)對(duì)ReactElement類型判斷而進(jìn)行不同的操作;ReactElement類型包含三種即:文本、Dom、組件;
每個(gè)類型的元素更新處理方式:
自定義元素的更新,主要是更新render出的節(jié)點(diǎn),做甩手掌柜交給render出的節(jié)點(diǎn)的對(duì)應(yīng)component去管理更新。
text節(jié)點(diǎn)的更新很簡(jiǎn)單,直接更新文案。
瀏覽器基本元素的更新,分為兩塊:
更新屬性,對(duì)比出前后屬性的不同,局部更新。并且處理特殊屬性,比如事件綁定。
子節(jié)點(diǎn)的更新,子節(jié)點(diǎn)更新主要是找出差異對(duì)象,找差異對(duì)象的時(shí)候也會(huì)使用上面的shouldUpdateReactComponent來(lái)判斷,如果是可以直接更新的就會(huì)遞歸調(diào)用子節(jié)點(diǎn)的更新,這樣也會(huì)遞歸查找差異對(duì)象。不可直接更新的刪除之前的對(duì)象或添加新的對(duì)象。之后根據(jù)差異對(duì)象操作dom元素(位置變動(dòng),刪除,添加等)。
事實(shí)上Diff算法只被調(diào)用于React更新階段的DOM元素更新過(guò)程;為什么這么說(shuō)?
1、 如果為更新文本類型,內(nèi)容不同就直接更新替換,并不會(huì)調(diào)用復(fù)雜的Diff算法:
ReactDOMTextComponent.prototype.receiveComponent(nextText, transaction) { //與之前保存的字符串比較 if (nextText !== this._currentElement) { this._currentElement = nextText; var nextStringText = "" + nextText; if (nextStringText !== this._stringText) { this._stringText = nextStringText; var commentNodes = this.getHostNode(); // 替換文本元素 DOMChildrenOperations.replaceDelimitedText( commentNodes[0], commentNodes[1], nextStringText ); } } }
2、對(duì)于自定義組件元素:
class Tab extends Component { constructor(props) { super(props); this.state = { index: 1, } } shouldComponentUpdate() { .... } render() { return () } }item1
item1
需要明確的是,何為組件,可以說(shuō)組件只不過(guò)是一段Html結(jié)構(gòu)的包裝容器,并且具備管理這段Html結(jié)構(gòu)的狀態(tài)等能力;
如上述Tab組件:它的實(shí)質(zhì)內(nèi)容就是render函數(shù)返回的Html結(jié)構(gòu),而我們所說(shuō)的Tab類就是這段Html結(jié)構(gòu)的包裝容器(可以理解為一個(gè)包裝盒子);
在React渲染機(jī)制圖中可以看到,自定義組件的最后結(jié)合React Diff優(yōu)化策略一(不同類的兩個(gè)組件具備不同的結(jié)構(gòu))
3、基本元素:
ReactDOMComponent.prototype.receiveComponent = function(nextElement, transaction, context) { var prevElement = this._currentElement; this._currentElement = nextElement; this.updateComponent(transaction, prevElement, nextElement, context); } ReactDOMComponent.prototype.updateComponent = function(transaction, prevElement, nextElement, context) { //需要多帶帶的更新屬性 this._updateDOMProperties(lastProps, nextProps, transaction, isCustomComponentTag); //再更新子節(jié)點(diǎn) this._updateDOMChildren( lastProps, nextProps, transaction, context ); // ...... }
在this._updateDOMChildren方法內(nèi)部才調(diào)用了diff算法。
3、React中Diff算法的實(shí)現(xiàn)_updateChildren: function(nextNestedChildrenElements, transaction, context) { var prevChildren = this._renderedChildren; var removedNodes = {}; var mountImages = []; // 獲取新的子元素?cái)?shù)組 var nextChildren = this._reconcilerUpdateChildren( prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context ); if (!nextChildren && !prevChildren) { return; } var updates = null; var name; var nextIndex = 0; var lastIndex = 0; var nextMountIndex = 0; var lastPlacedNode = null; for (name in nextChildren) { if (!nextChildren.hasOwnProperty(name)) { continue; } var prevChild = prevChildren && prevChildren[name]; var nextChild = nextChildren[name]; if (prevChild === nextChild) { // 同一個(gè)引用,說(shuō)明是使用的同一個(gè)component,所以我們需要做移動(dòng)的操作 // 移動(dòng)已有的子節(jié)點(diǎn) // NOTICE:這里根據(jù)nextIndex, lastIndex決定是否移動(dòng) updates = enqueue( updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex) ); // 更新lastIndex lastIndex = Math.max(prevChild._mountIndex, lastIndex); // 更新component的.mountIndex屬性 prevChild._mountIndex = nextIndex; } else { if (prevChild) { // 更新lastIndex lastIndex = Math.max(prevChild._mountIndex, lastIndex); } // 添加新的子節(jié)點(diǎn)在指定的位置上 updates = enqueue( updates, this._mountChildAtIndex( nextChild, mountImages[nextMountIndex], lastPlacedNode, nextIndex, transaction, context ) ); nextMountIndex++; } // 更新nextIndex nextIndex++; lastPlacedNode = ReactReconciler.getHostNode(nextChild); } // 移除掉不存在的舊子節(jié)點(diǎn),和舊子節(jié)點(diǎn)和新子節(jié)點(diǎn)不同的舊子節(jié)點(diǎn) for (name in removedNodes) { if (removedNodes.hasOwnProperty(name)) { updates = enqueue( updates, this._unmountChild(prevChildren[name], removedNodes[name]) ); } } }5、基于中Diff的開(kāi)發(fā)建議
基于tree diff:
開(kāi)發(fā)組件時(shí),注意保持DOM結(jié)構(gòu)的穩(wěn)定;即,盡可能少地動(dòng)態(tài)操作DOM結(jié)構(gòu),尤其是移動(dòng)操作。
當(dāng)節(jié)點(diǎn)數(shù)過(guò)大或者頁(yè)面更新次數(shù)過(guò)多時(shí),頁(yè)面卡頓的現(xiàn)象會(huì)比較明顯。
這時(shí)可以通過(guò) CSS 隱藏或顯示節(jié)點(diǎn),而不是真的移除或添加 DOM 節(jié)點(diǎn)。
基于component diff:
注意使用 shouldComponentUpdate() 來(lái)減少組件不必要的更新。
對(duì)于類似的結(jié)構(gòu)應(yīng)該盡量封裝成組件,既減少代碼量,又能減少component diff的性能消耗。
基于element diff:
對(duì)于列表結(jié)構(gòu),盡量減少類似將最后一個(gè)節(jié)點(diǎn)移動(dòng)到列表首部的操作,當(dāng)節(jié)點(diǎn)數(shù)量過(guò)大或更新操作過(guò)于頻繁時(shí),在一定程度上會(huì)影響 React 的渲染性能。
接下來(lái)手動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Diff算法即將更新,敬請(qǐng)期待~~~
“積跬步、行千里”—— 持續(xù)更新中~,喜歡留下個(gè)贊哦!
往期經(jīng)典好文:
團(tuán)隊(duì)合作必備的Git操作
談?wù)凧s前端規(guī)范化
從React渲染流程分析Diff算法
相關(guān)專欄推薦:
React學(xué)習(xí)之路
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98014.html
摘要:并且處理特殊屬性,比如事件綁定。之后根據(jù)差異對(duì)象操作元素位置變動(dòng),刪除,添加等。當(dāng)節(jié)點(diǎn)數(shù)過(guò)大或者頁(yè)面更新次數(shù)過(guò)多時(shí),頁(yè)面卡頓的現(xiàn)象會(huì)比較明顯?;谧⒁馐褂脕?lái)減少組件不必要的更新。 1、什么是Diff算法 傳統(tǒng)Diff:diff算法即差異查找算法;對(duì)于Html DOM結(jié)構(gòu)即為tree的差異查找算法;而對(duì)于計(jì)算兩顆樹(shù)的差異時(shí)間復(fù)雜度為O(n^3),顯然成本太高,React不可能采用這種...
摘要:算法的本質(zhì)是對(duì)傳統(tǒng)遍歷算法的優(yōu)化策略用三大策略將復(fù)雜度轉(zhuǎn)化為復(fù)雜度策略一中節(jié)點(diǎn)跨層級(jí)的移動(dòng)操作特別少,可以忽略不計(jì)。當(dāng)節(jié)點(diǎn)處于同一層級(jí)時(shí),提供三種節(jié)點(diǎn)操作刪除插入移動(dòng)。在舊的節(jié)點(diǎn)中的,它的,不滿足的條件,因此不做移動(dòng)操作。 一、react diff算法 diff算法的作用 計(jì)算出Virtual DOM中真正變化的部分,并只針對(duì)該部分進(jìn)行原生DOM操作,而非重新渲染整個(gè)頁(yè)面。 傳統(tǒng)di...
摘要:對(duì)同一層級(jí)的子節(jié)點(diǎn)進(jìn)行處理時(shí),會(huì)根據(jù)進(jìn)行簡(jiǎn)要的復(fù)用。二性能優(yōu)化方案由于中性能主要耗費(fèi)在于階段的算法,因此性能優(yōu)化也主要針對(duì)算法。此時(shí)最常用的優(yōu)化方案即為方法?;蛘咧苯邮褂?,原理一致。 一、從React原理談起 react是什么? showImg(https://segmentfault.com/img/bVbcYvf?w=1140&h=384); react是用于構(gòu)建用戶界面的JS框架...
摘要:對(duì)同一層級(jí)的子節(jié)點(diǎn)進(jìn)行處理時(shí),會(huì)根據(jù)進(jìn)行簡(jiǎn)要的復(fù)用。或者直接使用,原理一致。 一、從React原理談起 react是什么? showImg(https://segmentfault.com/img/bVbcYvf?w=1140&h=384); react是用于構(gòu)建用戶界面的JS框架。因此react只負(fù)責(zé)解決view層的渲染。 react做了什么? Virtual Dom模型 生命周期...
閱讀 1638·2019-08-30 15:54
閱讀 2384·2019-08-30 15:52
閱讀 2076·2019-08-29 15:33
閱讀 3050·2019-08-28 17:56
閱讀 3248·2019-08-26 13:54
閱讀 1683·2019-08-26 12:16
閱讀 2457·2019-08-26 11:51
閱讀 1656·2019-08-26 10:26