溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎樣深入理解react

發(fā)布時間:2021-11-09 17:42:48 來源:億速云 閱讀:126 作者:柒染 欄目:web開發(fā)

這篇文章給大家介紹怎樣深入理解react,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

理解ReactElement和ReactClass的概念

首先讓我們理解兩個概念:

ReactElement

一個描述DOM節(jié)點(diǎn)或component實(shí)例的字面級對象。它包含一些信息,包括組件類型type和屬性props。就像一個描述DOM節(jié)點(diǎn)的元素(虛擬節(jié)點(diǎn))。它們可以被創(chuàng)建通過React.createElement方法或jsx寫法

分為DOM Element和Component Elements兩類:

  • DOM Elements

當(dāng)節(jié)點(diǎn)的type屬性為字符串時,它代表是普通的節(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屬性為一個函數(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

ReactClass是平時我們寫的Component組件(類或函數(shù)),例如上面的Button類。ReactClass實(shí)例化后調(diào)用render方法可返回DOM  Element。

react渲染過程怎樣深入理解react

過程理解:

// element是 Component Elements ReactDOM.render({   type: Form,   props: {     isSubmitted: false,     buttonText: 'OK!'   } }, document.getElementById('root'));
  1. 調(diào)用React.render方法,將我們的element根虛擬節(jié)點(diǎn)渲染到container元素中。element可以是一個字符串文本元素,也可以是如上介紹的ReactElement(分為DOM  Elements, Component Elements)。

  2. 根據(jù)element的類型不同,分別實(shí)例化ReactDOMTextComponent, ReactDOMComponent,  ReactCompositeComponent類。這些類用來管理ReactElement,負(fù)責(zé)將不同的ReactElement轉(zhuǎn)化成DOM(mountComponent方法),負(fù)責(zé)更新DOM(receiveComponent方法,updateComponent方法,  如下會介紹)等。

  3. ReactCompositeComponent實(shí)例調(diào)用mountComponent方法后內(nèi)部調(diào)用render方法,返回了DOM  Elements。再對如圖的步驟2?遞歸。

react更新機(jī)制

怎樣深入理解react

每個類型的元素都要處理好自己的更新:

  1. 自定義元素的更新,主要是更新render出的節(jié)點(diǎn),做甩手掌柜交給render出的節(jié)點(diǎn)的對應(yīng)component去管理更新。

  2. text節(jié)點(diǎn)的更新很簡單,直接更新文案。

  3. 瀏覽器基本元素的更新,分為兩塊:

  • 先是更新屬性,對比出前后屬性的不同,局部更新。并且處理特殊屬性,比如事件綁定。

  • 然后是子節(jié)點(diǎn)的更新,子節(jié)點(diǎn)更新主要是找出差異對象,找差異對象的時候也會使用上面的shouldUpdateReactComponent來判斷,如果是可以直接更新的就會遞歸調(diào)用子節(jié)點(diǎn)的更新,這樣也會遞歸查找差異對象。不可直接更新的刪除之前的對象或添加新的對象。之后根據(jù)差異對象操作dom元素(位置變動,刪除,添加等)。

***步:調(diào)用this.setState

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 ) {    // 簡寫.....         // 不是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,淺層對比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對象類型是字符串或數(shù)字,新的ReactElement對象類型也是字符串或數(shù)字,則需要更新,新的ReactElement對象類型是對象,則不應(yīng)該更新,直接替換。     return (nextType === 'string' || nextType === 'number');   } else {       // 如果先前的ReactElement對象類型是對象,新的ReactElement對象類型也是對象,并且標(biāo)簽類型和key值相同,則需要更新     return (       nextType === 'object' &&       prevElement.type === nextElement.type &&       prevElement.key === nextElement.key     );   } }

文本元素:

receiveComponent方法源碼

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         );       }     }   }

基本元素:

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) {     // 略.....     //需要單獨(dú)的更新屬性     this._updateDOMProperties(lastProps, nextProps, transaction, isCustomComponentTag);     //再更新子節(jié)點(diǎn)     this._updateDOMChildren(       lastProps,       nextProps,       transaction,       context     );      // ...... }

this._updateDOMChildren方法內(nèi)部調(diào)用diff算法,請看下一節(jié)........

react Diff算法

怎樣深入理解react

diff算法源碼

_updateChildren: function(nextNestedChildrenElements, transaction, context) {     var prevChildren = this._renderedChildren;     var removedNodes = {};     var mountImages = [];          // 獲取新的子元素數(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) {           // 同一個引用,說明是使用的同一個component,所以我們需要做移動的操作           // 移動已有的子節(jié)點(diǎn)           // NOTICE:這里根據(jù)nextIndex, lastIndex決定是否移動         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é)點(diǎn)。在UI方面,不需要立刻更新視圖,而是生成虛擬DOM后統(tǒng)一渲染。

  • 組件機(jī)制。各個組件獨(dú)立管理,層層嵌套,互不影響,react內(nèi)部實(shí)現(xiàn)的渲染功能。

  • 差異算法。根據(jù)基本元素的key值,判斷是否遞歸更新子節(jié)點(diǎn),還是刪除舊節(jié)點(diǎn),添加新節(jié)點(diǎn)。

想要更好的利用react的虛擬DOM,diff算法的優(yōu)勢,我們需要正確的優(yōu)化、組織react頁面。例如將一個頁面render的ReactElement節(jié)點(diǎn)分解成多個組件。在需要優(yōu)化的組件手動添加  shouldComponentUpdate 來避免不需要的 re-render。

關(guān)于怎樣深入理解react就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI