摘要:根據(jù)的類(lèi)型不同,分別實(shí)例化類(lèi)。并且處理特殊屬性,比如事件綁定。之后根據(jù)差異對(duì)象操作元素位置變動(dòng),刪除,添加等。各個(gè)組件獨(dú)立管理層層嵌套,互不影響,內(nèi)部實(shí)現(xiàn)的渲染功能。根據(jù)基本元素的值,判斷是否遞歸更新子節(jié)點(diǎn),還是刪除舊節(jié)點(diǎn),添加新節(jié)點(diǎn)。
首先理解ReactElement和ReactClass的概念。想要更好的利用react的虛擬DOM,diff算法的優(yōu)勢(shì),我們需要正確的優(yōu)化、組織react頁(yè)面。
理解ReactElement和ReactClass的概念
ReactElement一個(gè)描述DOM節(jié)點(diǎn)或component實(shí)例的字面級(jí)對(duì)象。它包含一些信息,包括組件類(lèi)型type和屬性props。就像一個(gè)描述DOM節(jié)點(diǎn)的元素(虛擬節(jié)點(diǎn))。它們可以被創(chuàng)建通過(guò)React.createElement方法或jsx寫(xiě)法
分為DOM Element和Component Elements兩類(lèi):ReactElementDOM Elements
當(dāng)節(jié)點(diǎn)的type屬性為字符串時(shí),它代表是普通的節(jié)點(diǎn),如div,span
{ type: "button", props: { className: "button button-blue", children: { type: "b", props: { children: "OK!" } } } }Component Elements
當(dāng)節(jié)點(diǎn)的type屬性為一個(gè)函數(shù)或一個(gè)類(lèi)時(shí),它代表自定義的節(jié)點(diǎn)
class Button extends React.Component { render() { const { children, color } = this.props; return { type: "button", props: { className: "button button-" + color, children: { type: "b", props: { children: children } } } }; } } // Component Elements { type: Button, props: { color: "blue", children: "OK!" } }ReactClass是平時(shí)我們寫(xiě)的Component組件(類(lèi)或函數(shù)),例如上面的Button類(lèi)。ReactClass實(shí)例化后調(diào)用render方法可返回DOM Element。
react渲染過(guò)程過(guò)程理解:
// element是 Component Elements ReactDOM.render({ type: Form, props: { isSubmitted: false, buttonText: "OK!" } }, document.getElementById("root"));react更新機(jī)制調(diào)用React.render方法,將我們的element根虛擬節(jié)點(diǎn)渲染到container元素中。element可以是一個(gè)字符串文本元素,也可以是如上介紹的ReactElement(分為DOM Elements, Component Elements)。
根據(jù)element的類(lèi)型不同,分別實(shí)例化ReactDOMTextComponent, ReactDOMComponent, ReactCompositeComponent類(lèi)。這些類(lèi)用來(lái)管理ReactElement,負(fù)責(zé)將不同的ReactElement轉(zhuǎn)化成DOM(mountComponent方法),負(fù)責(zé)更新DOM(receiveComponent方法,updateComponent方法, 如下會(huì)介紹)等。
ReactCompositeComponent實(shí)例調(diào)用mountComponent方法后內(nèi)部調(diào)用render方法,返回了DOM Elements。
每個(gè)類(lèi)型的元素都要處理好自己的更新:
第一步:調(diào)用this.setState自定義元素的更新,主要是更新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),刪除,添加等)。
ReactClass.prototype.setState = function(newState) { //this._reactInternalInstance是ReactCompositeComponent的實(shí)例 this._reactInternalInstance.receiveComponent(null, newState); }第二步:調(diào)用內(nèi)部receiveComponent方法這里主要分三種情況,文本元素,基本元素,自定義元素。
自定義元素:
receiveComponent方法源碼:// receiveComponent方法 ReactCompositeComponent.prototype.receiveComponent = function(nextElement, transaction, nextContext) { var prevElement = this._currentElement; var prevContext = this._context; this._pendingElement = null; this.updateComponent( transaction, prevElement, nextElement, prevContext, nextContext ); }updateComponent方法源碼
// updateComponent方法 ReactCompositeComponent.prototype.updateComponent = function( transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext ) { // 簡(jiǎn)寫(xiě)..... // 不是state更新而是props更新 if (prevParentElement !== nextParentElement) { willReceive = true; } if (willReceive && inst.componentWillReceiveProps) { // 調(diào)用生命周期componentWillReceiveProps方法 } // 是否更新元素 if (inst.shouldComponentUpdate) { // 如果提供shouldComponentUpdate方法 shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext); } else { if (this._compositeType === CompositeTypes.PureClass) { // 如果是PureClass,淺層對(duì)比props和state shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); } } if (shouldUpdate) { // 更新元素 this._performComponentUpdate( nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext ); } else { // 不更新元素,但仍然設(shè)置props和state this._currentElement = nextParentElement; this._context = nextUnmaskedContext; inst.props = nextProps; inst.state = nextState; inst.context = nextContext; } // ....... }內(nèi)部_performComponentUpdate方法源碼
function shouldUpdateReactComponent(prevElement, nextElement){ var prevEmpty = prevElement === null || prevElement === false; var nextEmpty = nextElement === null || nextElement === false; if (prevEmpty || nextEmpty) { return prevEmpty === nextEmpty; } var prevType = typeof prevElement; var nextType = typeof nextElement; if (prevType === "string" || prevType === "number") { // 如果先前的ReactElement對(duì)象類(lèi)型是字符串或數(shù)字,新的ReactElement對(duì)象類(lèi)型也是字符串或數(shù)字, return (nextType === "string" || nextType === "number"); } else { // 如果先前的ReactElement對(duì)象類(lèi)型是對(duì)象,新的ReactElement對(duì)象類(lèi)型也是對(duì)象,并且標(biāo)簽類(lèi)型和key值相同,則需要更新 return ( nextType === "object" && prevElement.type === nextElement.type && prevElement.key === nextElement.key ); } }基本元素
receiveComponent方法源碼ReactDOMComponent.prototype.receiveComponent = function(nextElement, transaction, context) { var prevElement = this._currentElement; this._currentElement = nextElement; this.updateComponent(transaction, prevElement, nextElement, context); }updateComponent方法源碼
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算法。
react Diff算法diff算法源碼
_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]) ); } } }react的優(yōu)點(diǎn)與總結(jié) 優(yōu)點(diǎn):總結(jié):虛擬節(jié)點(diǎn)。在UI方面,不需要立刻更新視圖,而是生成虛擬DOM后統(tǒng)一渲染。
組件機(jī)制。各個(gè)組件獨(dú)立管理,層層嵌套,互不影響,react內(nèi)部實(shí)現(xiàn)的渲染功能。
差異算法。根據(jù)基本元素的key值,判斷是否遞歸更新子節(jié)點(diǎn),還是刪除舊節(jié)點(diǎn),添加新節(jié)點(diǎn)。
想要更好的利用react的虛擬DOM,diff算法的優(yōu)勢(shì),我們需要正確的優(yōu)化、組織react頁(yè)面。例如將一個(gè)頁(yè)面render的ReactElement節(jié)點(diǎn)分解成多個(gè)組件。在需要優(yōu)化的組件手動(dòng)添加 shouldComponentUpdate 來(lái)避免不需要的 re-render。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105570.html
摘要:學(xué)習(xí)之道簡(jiǎn)體中文版通往實(shí)戰(zhàn)大師之旅掌握最簡(jiǎn)單,且最實(shí)用的教程。前言學(xué)習(xí)之道這本書(shū)使用路線圖中的精華部分用于傳授,并將其融入一個(gè)獨(dú)具吸引力的真實(shí)世界的具體代碼實(shí)現(xiàn)。完美展現(xiàn)了的優(yōu)雅。膜拜的學(xué)習(xí)之道是必讀的一本書(shū)。 《React 學(xué)習(xí)之道》The Road to learn React (簡(jiǎn)體中文版) 通往 React 實(shí)戰(zhàn)大師之旅:掌握 React 最簡(jiǎn)單,且最實(shí)用的教程。 showIm...
摘要:前言?xún)善恼聦W(xué)完了基礎(chǔ)篇原理篇,接下去便是實(shí)踐的過(guò)程,這個(gè)實(shí)踐我們使用了如下技術(shù)棧去實(shí)現(xiàn)一套任務(wù)管理系統(tǒng),源碼就不公開(kāi)了等穩(wěn)定后再發(fā)布。后續(xù)我所在的公司網(wǎng)關(guān)團(tuán)隊(duì)會(huì)持續(xù)實(shí)踐,爭(zhēng)取貢獻(xiàn)出更多的解決方案。前言 兩篇文章學(xué)完了GraphQL(基礎(chǔ)篇, 原理篇),接下去便是實(shí)踐的過(guò)程,這個(gè)實(shí)踐我們使用了如下技術(shù)棧去實(shí)現(xiàn)一套任務(wù)管理系統(tǒng),源碼就不公開(kāi)了, 等穩(wěn)定后再發(fā)布。效果如下: showImg(ht...
摘要:作為大型應(yīng)用狀態(tài)管理最常用的工具。它是一個(gè)應(yīng)用數(shù)據(jù)流框架,與框架類(lèi)似。這是觸發(fā)變化的惟一途徑。在這個(gè)函數(shù)內(nèi)部,被調(diào)用,其作用是監(jiān)測(cè)是的。否則的話,認(rèn)為只是一個(gè)普通的,將通過(guò)也就是進(jìn)一步分發(fā)。到此源碼的主要部分學(xué)習(xí)結(jié)束。 Redux作為大型React應(yīng)用狀態(tài)管理最常用的工具。它是一個(gè)應(yīng)用數(shù)據(jù)流框架,與Flux框架類(lèi)似。它是零依賴(lài)的,可以配合其他框架或者類(lèi)庫(kù)一起使用。雖然在平時(shí)的工作中很多...
摘要:語(yǔ)法將語(yǔ)法直接加入到代碼中,再通過(guò)翻譯器裝換到純后由瀏覽器執(zhí)行。事實(shí)上,并不需要花精力學(xué)習(xí)。可以說(shuō),基本語(yǔ)法基本被囊括了,但也有少許不同。明確的數(shù)據(jù)流動(dòng)。這條原則讓組件之間的關(guān)系變得簡(jiǎn)單且可預(yù)測(cè)。使用獲取和顯示回調(diào)。 JSX語(yǔ)法 JSX將HTML語(yǔ)法直接加入到JavaScript代碼中,再通過(guò)翻譯器裝換到純JavaScript后由瀏覽器執(zhí)行。在實(shí)際開(kāi)發(fā)中,JSX在產(chǎn)品打包階段都已經(jīng)編...
摘要:是用戶(hù)建立客戶(hù)端應(yīng)用的前端架構(gòu),它通過(guò)利用一個(gè)單向的數(shù)據(jù)流補(bǔ)充了的組合視圖組件,這更是一種模式而非正式框架,你能夠無(wú)需許多新代碼情況下立即開(kāi)始使用。結(jié)構(gòu)和數(shù)據(jù)流一個(gè)單向數(shù)據(jù)流是模式的核心。 Flux是Facebook用戶(hù)建立客戶(hù)端Web應(yīng)用的前端架構(gòu),它通過(guò)利用一個(gè)單向的數(shù)據(jù)流補(bǔ)充了React的組合視圖組件,這更是一種模式而非正式框架,你能夠無(wú)需許多新代碼情況下立即開(kāi)始使用Flux。 ...
閱讀 3306·2023-04-26 02:40
閱讀 4653·2021-09-22 15:22
閱讀 1596·2021-09-22 10:02
閱讀 3490·2021-08-11 10:23
閱讀 1399·2019-08-30 15:55
閱讀 2500·2019-08-30 12:48
閱讀 593·2019-08-30 11:04
閱讀 710·2019-08-29 16:29