国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入理解react(源碼分析)

villainhr / 2752人閱讀

摘要:渲染過程過程理解是調(diào)用方法,將我們的根虛擬節(jié)點渲染到元素中。根據(jù)的類型不同,分別實例化類。并且處理特殊屬性,比如事件綁定。之后根據(jù)差異對象操作元素位置變動,刪除,添加等。各個組件獨立管理層層嵌套,互不影響,內(nèi)部實現(xiàn)的渲染功能。

原文鏈接

理解ReactElement和ReactClass的概念

ReactElement

ReactClass

react渲染過程

react更新機(jī)制

reactdiff算法

react的優(yōu)點與總結(jié)

理解ReactElement和ReactClass的概念

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

ReactElement

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

分為DOM ElementComponent Elements兩類:

DOM Elements

當(dāng)節(jié)點的type屬性為字符串時,它代表是普通的節(jié)點,如div,span

{
  type: "button",
  props: {
    className: "button button-blue",
    children: {
      type: "b",
      props: {
        children: "OK!"
      }
    }
  }
}

Component Elements

當(dāng)節(jié)點的type屬性為一個函數(shù)或一個類時,它代表自定義的節(jié)點

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實例化后調(diào)用render方法可返回DOM Element

react渲染過程

過程理解:

// element是 Component Elements
ReactDOM.render({
  type: Form,
  props: {
    isSubmitted: false,
    buttonText: "OK!"
  }
}, document.getElementById("root"));

調(diào)用React.render方法,將我們的element根虛擬節(jié)點渲染到container元素中。element可以是一個字符串文本元素,也可以是如上介紹的ReactElement(分為DOM Elements, Component Elements)。

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

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

react更新機(jī)制

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

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

text節(jié)點的更新很簡單,直接更新文案。

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

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

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

第一步:調(diào)用this.setState
ReactClass.prototype.setState = function(newState) {
    //this._reactInternalInstance是ReactCompositeComponent的實例
    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方法源碼

   // 內(nèi)部_updateRenderedComponentWithNextElement方法
    ReactCompositeComponent.prototype._updateRenderedComponentWithNextElement = function() {
    
    // 判定兩個element需不需要更新
    if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
      // 如果需要更新,就繼續(xù)調(diào)用子節(jié)點的receiveComponent的方法,傳入新的element更新子節(jié)點。
      ReactReconciler.receiveComponent(
        prevComponentInstance,
        nextRenderedElement,
        transaction,
        this._processChildContext(context)
      );
    } else {
      // 卸載之前的子節(jié)點,安裝新的子節(jié)點
      var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
      ReactReconciler.unmountComponent(
        prevComponentInstance,
        safely,
        false /* skipLifecycle */
      );

      var nodeType = ReactNodeTypes.getType(nextRenderedElement);
      this._renderedNodeType = nodeType;
      var child = this._instantiateReactComponent(
        nextRenderedElement,
        nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
      );
      this._renderedComponent = child;

      var nextMarkup = ReactReconciler.mountComponent(
        child,
        transaction,
        this._hostParent,
        this._hostContainerInfo,
        this._processChildContext(context),
        debugID
      );
    
    }

shouldUpdateReactComponent函數(shù)源碼

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) {
    // 略.....
    //需要多帶帶的更新屬性
    this._updateDOMProperties(lastProps, nextProps, transaction, isCustomComponentTag);
    //再更新子節(jié)點
    this._updateDOMChildren(
      lastProps,
      nextProps,
      transaction,
      context
    );

    // ......
}

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

react Diff算法

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é)點
          // 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é)點在指定的位置上
        updates = enqueue(
          updates,
          this._mountChildAtIndex(
            nextChild,
            mountImages[nextMountIndex],
            lastPlacedNode,
            nextIndex,
            transaction,
            context
          )
        );
        
        
        nextMountIndex++;
      }
      
      // 更新nextIndex
      nextIndex++;
      lastPlacedNode = ReactReconciler.getHostNode(nextChild);
    }
    
    // 移除掉不存在的舊子節(jié)點,和舊子節(jié)點和新子節(jié)點不同的舊子節(jié)點
    for (name in removedNodes) {
      if (removedNodes.hasOwnProperty(name)) {
        updates = enqueue(
          updates,
          this._unmountChild(prevChildren[name], removedNodes[name])
        );
      }
    }
  }
react的優(yōu)點與總結(jié) 優(yōu)點

虛擬節(jié)點。在UI方面,不需要立刻更新視圖,而是生成虛擬DOM后統(tǒng)一渲染。

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

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

總結(jié)

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

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/91049.html

相關(guān)文章

  • React深入深入分析虛擬DOM的渲染原理和特性

    摘要:導(dǎo)讀的虛擬和算法是的非常重要的核心特性,這部分源碼也非常復(fù)雜,理解這部分知識的原理對更深入的掌握是非常必要的。本篇文章從源碼出發(fā),分析虛擬的核心渲染原理首次渲染,以及對它做的性能優(yōu)化點。 showImg(https://segmentfault.com/img/remote/1460000018891457); 導(dǎo)讀 React的虛擬DOM和Diff算法是React的非常重要的核心特性...

    BlackMass 評論0 收藏0
  • 前端學(xué)習(xí)資源整理

    稍微整理了一下自己平時看到的前端學(xué)習(xí)資源,分享給大家。 html MDN:Mozilla開發(fā)者網(wǎng)絡(luò) SEO:前端開發(fā)中的SEO css 張鑫旭:張鑫旭的博客 css精靈圖:css精靈圖實踐 柵格系統(tǒng):詳解CSS中的柵格系統(tǒng) 媒體查詢:css媒體查詢用法 rem布局:手機(jī)端頁面自適應(yīng)布局 移動前端開發(fā)之viewport的深入理解:深入理解viewport 淘寶前端布局:手機(jī)淘寶移動端布局 fl...

    siberiawolf 評論0 收藏0
  • jquery源碼分析

    摘要:前言隨著前端的不斷發(fā)展,很多開發(fā)人員已經(jīng)開始使用等框架,但是很少有人去深入分析以及的源碼本人也是,至今還停留在使用的層面。最近還在寫一些的筆記,有興趣的小白也可以看下我的博客文章源碼分析地址 前言 隨著前端的不斷發(fā)展,很多開發(fā)人員已經(jīng)開始使用react、vue等web框架,但是很少有人去深入分析vue以及react的源碼(本人也是,至今還停留在使用的層面)。框架的使用勢必會有更新迭代的...

    SHERlocked93 評論0 收藏0
  • React 內(nèi)部機(jī)制探秘 - React Component 和 Element(文末附彩蛋demo

    摘要:內(nèi)部機(jī)制探秘和文末附彩蛋和源碼這篇文章比較偏基礎(chǔ),但是對入門內(nèi)部機(jī)制和實現(xiàn)原理卻至關(guān)重要。當(dāng)然也需要明白一些淺顯的內(nèi)部工作機(jī)制。當(dāng)改變出現(xiàn)時,相比于真實更新虛擬的性能優(yōu)勢非常明顯。直到最終,會得到完整的表述樹的對象。 React 內(nèi)部機(jī)制探秘 - React Component 和 Element(文末附彩蛋demo和源碼) 這篇文章比較偏基礎(chǔ),但是對入門 React 內(nèi)部機(jī)制和實現(xiàn)原...

    wenshi11019 評論0 收藏0
  • 深入理解JavaScript

    摘要:深入之繼承的多種方式和優(yōu)缺點深入系列第十五篇,講解各種繼承方式和優(yōu)缺點。對于解釋型語言例如來說,通過詞法分析語法分析語法樹,就可以開始解釋執(zhí)行了。 JavaScript深入之繼承的多種方式和優(yōu)缺點 JavaScript深入系列第十五篇,講解JavaScript各種繼承方式和優(yōu)缺點。 寫在前面 本文講解JavaScript各種繼承方式和優(yōu)缺點。 但是注意: 這篇文章更像是筆記,哎,再讓我...

    myeveryheart 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<