美文网首页
【译】了解React源代码-UI更新(单个DOM)8

【译】了解React源代码-UI更新(单个DOM)8

作者: nextChallenger | 来源:发表于2019-10-15 20:28 被阅读0次

    【译】了解React源代码-初始渲染(简单组件)1
    【译】了解React源代码-初始渲染(简单组件)2
    【译】了解React源代码-初始渲染(简单组件)3
    【译】了解React源代码-初始渲染(类组件)4
    【译】了解React源代码-初始渲染(类组件)5
    【译】了解React源代码-UI更新(事务)6
    【译】了解React源代码-UI更新(事务)7
    【译】了解React源代码-UI更新(单个DOM)8
    【译】了解React源代码-UI更新(DOM树)9


    UI更新本质上就是数据更改。 React提供了一种直观,直观的方式对前端App进行编程,因为大多数活动部件都以状态形式收敛,并且大多数UI任务都可以通过一个操作完成

    …,我的意思是,只有一个方法setState()。 在本文中,我们将展开setState()实现,并通过对单个DOM元素进行变异来窥视diffing算法。

    Before we get started, I would like to respond to one common feedback from readers: “why 15.x, why not fiber?”Well, simply put, because synchronous rendering is still alive. Thus, the code base (a.k.a., stack reconciler) specifically designed for synchronous rendering, in my opinion, offers an easier albeit solid ground to establish an initial understanding.
    在开始之前,我想回应读者的一个普遍反馈:“为什么使用15.x,为什么不使用 fiber?”好吧,简单地说,因为同步渲染仍然有效。 因此,我认为,专为同步渲染而设计的代码库(又称堆栈协调器)为建立初步理解提供了更容易的基础。

    首先,让我们从{第四篇}扩展一个例子

    class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          desc: 'start',
          color: 'blue'
        };
    
        this.timer = setTimeout(
          () => this.tick(),
          5000
        );
      }
    
      tick() {
        this.setState({
          desc: 'end',
          color: 'green'
        });
      }
    
      render() {
        return (
          <div className="App">
            <div className="App-header">
              <img src="main.jpg" className="App-logo" alt="logo" />
              <h1> "Welcom to React" </h1>
            </div>
            <p className="App-intro" style={{color: this.state.color}}>
              { this.state.desc }
            </p>
          </div>
        );
      }
    }
    
    export default App;
    

    与{第四篇}中使用的App组件相比,新版本在组件构建5秒后将style prop添加到<p>节点,并将setState()desc设置为“ end”,将颜色设置为“ green”

    {第四篇}已讨论了App的实例化。

    ctl-f “setState”In the same article, I also mentioned ReactInstanceMap, a back link (from the external ReactComponent instance) to the internal ReactCompositeComponent[ins], which will be used very soon.
    ctl -f“ setState”在同一篇文章中,我还提到了ReactInstanceMap,这是一个指向内部ReactCompositeComponent [ins]的反向链接(从外部ReactComponent实例)。

    在这里,我粘贴数据结构作为提醒。

    Figure-I.png

    Before transactions

    我们从setState()方法主体开始:

    ReactComponent.prototype.setState = function (
      partialState,
      callback
    ) {
      // scr: ---> sanity check
      this.updater.enqueueSetState(this, partialState);
      if (callback) {
      // scr: ---> no callbak
      }
    };
    
    ReactComponent@isomorphic/modern/class/ReactBaseClasses.js
    

    是的,setState()是从ReactComponent继承的。

    但是等等,this.updater是什么? 它不是在构造函数中设置为ReactNoopUpdateQueue,并且是no-op空操作吗? 实际上,我相信通过对Transaction(s)和实例池的了解,您可以从上述的ReactComponent实例化(第四篇)中追溯,您将能够很容易地找到this.updater的来源。

    I will leave this question open so we can move faster to the core part —virtual DOM and diffing algorithm
    我将保留这个问题,以便我们可以更快地进入核心部分-虚拟DOM和差异算法

    enqueueSetState: function (publicInstance, partialState) {
    // scr: DEV code
    
      // scr: ------------------------------------------------------> 1)
      var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
    
      if (!internalInstance) {
        return;
      }
    
      // scr: ------------------------------------------------------> 2)
      var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
      queue.push(partialState);
    
      // scr: ------------------------------------------------------> 3)
      enqueueUpdate(internalInstance);
    },
    
    ReactUpdateQueue@renderers/shared/stack/reconciler/ReactUpdateQueue.js
    

    1)这是从反向链接ReactInstanceMap获得内部ReactCompositeComponent [ins]的方法;

    function getInternalInstanceReadyForUpdate(
      publicInstance,
      callerName
    ) {
      var internalInstance = ReactInstanceMap.get(publicInstance);
    
    ... // scr: DEV code
    
      return internalInstance;
    }
    
    getInternalInstanceReadyForUpdate@renderers/shared/stack/reconciler/ReactUpdateQueue.js
    

    2)将一个数组(_pendingStateQueue)附加到ReactCompositeComponent [ins],并将更改后的状态{desc:'end',color:'green'}推入其中;

    3)开始Transaction(s){第六篇第七篇},

    ...
    function enqueueUpdate(internalInstance) {
      ReactUpdates.enqueueUpdate(internalInstance);
    }
    ...
    
    enqueueUpdate@renderers/shared/stack/reconciler/ReactUpdateQueue.js
    

    到目前为止的调用栈:

    |-ReactComponent.setState()
      |-ReactUpdateQueue.enqueueSetState()
        |-getInternalInstanceReadyForUpdate()
        |-enqueueUpdate()
          |-ReactUpdates.enqueueUpdate()
            |~~~
    

    在这里,我还将粘贴与交易相关的调用图作为提醒。

    In transactions

    Transaction(s)完全初始化之后的第一站是

    function runBatchedUpdates(transaction) {
      var len = transaction.dirtyComponentsLength;
    
    // scr: -----------------------------------> sanity check
    ...
    
      dirtyComponents.sort(mountOrderComparator);
    
      updateBatchNumber++;
    
      for (var i = 0; i < len; i++) {
        var component = dirtyComponents[i];
    
        var callbacks = component._pendingCallbacks;
        component._pendingCallbacks = null;
    
        // scr: ------------------------------> logging
    ...
    
        ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction, updateBatchNumber);
    
        // scr: ------------------------------> logging
        if (callbacks) { // scr: -------------> no callbacks
    ...
        }
      }
    }
    
    ReactUpdates@renderers/shared/stack/reconciler/ReactUpdates.js
    

    这次我们有一个dirtyComponents, ReactCompositeComponent [ins],它是ReactReconciler.performUpdateIfNecessary()的第一个参数。

    performUpdateIfNecessary: function (
      internalInstance,
      transaction,
      updateBatchNumber
    ) {
    // scr: DEV code
    ...
    
        internalInstance.performUpdateIfNecessary(transaction);
    
    // scr: DEV code
    ...
    }
    
    ReactReconciler@renderers/shared/stack/reconciler/ReactUpdates.js
    

    ReactReconciler类中的其他大多数方法一样,ReactReconciler.performUpdateIfNecessary()将调用组件的相同方法ReactCompositeComponent.performUpdateIfNecessary()

    it’s like a polymorphism in a more explicit way
    更明显地就像是多态

    performUpdateIfNecessary: function (transaction) {
      if (this._pendingElement != null) {
        // scr: -----------> condition not applied
    ...
      } else if (
        this._pendingStateQueue !== null || 
        this._pendingForceUpdate
      ) {
        this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
      } else {
        // scr: -----------> condition not applied
    ...
      }
    },
    
    ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js
    

    依次调用ReactCompositeComponent [ins] .updateComponent()。 请注意,_pendingStateQueue是在逻辑进入Transaction上下文之前设置的。

    updateComponent: function(
        transaction,
        prevParentElement,
        nextParentElement,
        prevUnmaskedContext,
        nextUnmaskedContext,
    ) {
      var inst = this._instance; // scr: ---------------------------> 1)
      // scr: sanity check and code that is not applicable this time
    ...
    
      // scr: ------------------------------------------------------> 2)    
      var nextState = this._processPendingState(nextProps, nextContext); 
    
      var shouldUpdate = true;
    
      if (!this._pendingForceUpdate) {
        if (inst.shouldComponentUpdate) { // scr: ------------------> 3)
          shouldUpdate = inst.shouldComponentUpdate(
            nextProps,
            nextState,
            nextContext,
          );
        } else {
          if (this._compositeType === CompositeTypes.PureClass) {
            // scr: ---------------> it is ImpureClass, not applicable
    ...
          }
        }
      }
    
      this._updateBatchNumber = null;
      if (shouldUpdate) {
        this._pendingForceUpdate = false;
        // Will set `this.props`, `this.state` and `this.context`.
        this._performComponentUpdate( // scr: --------------------> 4)
          nextParentElement,
          nextProps,
          nextState,
          nextContext,
          transaction,
          nextUnmaskedContext,
        );
      } else {
        // scr: code that is not applicable this time
    ...
      }
    },
    
    ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js
    

    1)从ReactCompositeComponent [ins] ._ instance {Figure-I}获得外部ReactComponent实例(App);

    2)使用Object.assign()合并ReactCompositeComponent [ins] ._ pendingStateQueue({desc:'end',color:'green'})中的部分状态和现有状态;

    _processPendingState: function(props, context) {
      // scr: -------> obtain the App {Figure-I}
      var inst = this._instance;
      var queue = this._pendingStateQueue;
      // scr: code that is not applicable this time
    ...
    
      var nextState = Object.assign({}, replace ? queue[0] : inst.state);
    
      for (var i = replace ? 1 : 0; i < queue.length; i++) {
        var partial = queue[i];
        Object.assign(
          nextState,
          typeof partial === 'function'
            ? partial.call(inst, nextState, props, context)
            : partial,
        );
      }
    
      return nextState;
    },
    
    ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js
    

    3)这是提供给开发人员的生命周期函数,以避免在setState()不更改关键状态的情况下执行协调(以下处理逻辑);

    Most likely you do not need this function
    最有可能您不需要此功能

    4)进入下一站。

    _performComponentUpdate: function(
        nextElement,
        nextProps,
        nextState,
        nextContext,
        transaction,
        unmaskedContext,
    ) {
      var inst = this._instance; // scr: {Figure-I}
      // scr: code that is not applicable this time
    ...
      // scr: invoke App's life cycle method if defined
      if (inst.componentWillUpdate) { 
        inst.componentWillUpdate(nextProps, nextState, nextContext);
      }
      // scr: code that is not applicable this time
    ...
        inst.state = nextState;
    ...
      this._updateRenderedComponent(transaction, unmaskedContext);
      // scr: queue App's life cycle method if defined
      if (hasComponentDidUpdate) {
    ...
      }
    },
    
    ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js
    

    只需将Appstate设置为新合并的状态即可。 并调用this._updateRenderedComponent(),它是差异算法的入口点。

    到目前为止的调用栈:

    ...
    |~~~
      |-runBatchedUpdates()
        |-performUpdateIfNecessary()
          |-ReactCompositeComponent[ins].performUpdateIfNecessary()
            |-this.updateComponent()
              |-this._processPendingState()
              |-this._performComponentUpdate()                     ___
                |-this._updateRenderedComponent()                   |
    ...                                                          diffing
    

    然后,逻辑处理到差分算法。

    Virtual DOM

    在开始研究Diffing算法之前,最好先对虚拟DOM的确切含义达成共识,因为该术语未出现在代码库中。

    在这里,我贴上{第五篇}中的图片作为提醒

    Figure-III.png

    ·ReactElement·是我们将要达成共识的虚拟DOM。 {第四篇第五篇}还讨论了虚拟DOM树的最初建立方式。

    In MVC terms ReactElements are modals which contain only data. On the other hand, ReactDOMComponents are controllers that offer actionable methods.
    用MVC术语来说,React Elements是仅包含数据的模态。 另一方面,ReactDOMComponent是提供可操作方法的控制器。

    Diffing

    上图给出了在{第四篇}中生成的旧的虚拟DOM树。

    ctl-f “in _renderValidatedComponent()”

    此步骤将基于更改后的状态,使用ReactCompositeComponent [ins] ._ renderValidatedComponent()生成一个新对象,以进行区分。

    _updateRenderedComponent: function (transaction, context) {
      var prevComponentInstance = this._renderedComponent; // scr: -> 1)
      // scr: ------------------------------------------------------> 2)
      var prevRenderedElement = prevComponentInstance._currentElement;
      // scr: create a new DOM tree
      var nextRenderedElement = this._renderValidatedComponent();
      var debugID = 0;
      // scr: DEV code
    ...
      if (shouldUpdateReactComponent( // scr: ----------------------> 3)
            prevRenderedElement,
            nextRenderedElement)
      ) {
        ReactReconciler.receiveComponent( // scr: ------------------> 5)
          prevComponentInstance,
          nextRenderedElement,
          transaction,
          this._processChildContext(context)
        );
      } else { // scr: ---------------------------------------------> 4)
      // scr: code that is not applicable this time
    ...
      }
    },
    
    ReactCompositeComponent@renderers/shared/stack/reconciler/ReactCompositeComponent.js
    

    1)通过ReactCompositeComponent [ins] {Figure-I}获得ReactDOMComponent [6]

    2)在App [ins] .render()中级联调用React.createElement()来创建新的DOM树{第四篇},其中唯一不同的DOM节点是:

    3)diffing算法的第一个比较是新旧根元素的类型之间的比较;

    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') {
        return nextType === 'string' || nextType === 'number';
      } else {
        return nextType === 'object' && prevElement.type === nextElement.type && prevElement.key === nextElement.key;
      }
    }
    
    shouldUpdateReactComponent@renderers/shared/shared/shouldUpdateReactComponent.js
    

    4)如果它们不相同,则从头开始构建新树-组件安装过程与{第五篇}中讨论的过程类似;

    whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch
    每当根元素具有不同类型时,React都会拆开旧树并从头开始构建新树

    5)如果相同,则开始DOM更新过程。

    receiveComponent: function (nextElement, transaction, context) {
      var prevElement = this._currentElement;
      this._currentElement = nextElement;
      this.updateComponent(transaction,
                           prevElement,
                           nextElement,
                           context);
    },
    
    updateComponent: function(
      transaction,
      prevElement,
      nextElement,
      context
    ) {
      var lastProps = prevElement.props;
      var nextProps = this._currentElement.props;
      // scr: code that is not applicable this time
    ...
      // scr: ------------------------------------------------------> 1)
      this._updateDOMProperties(lastProps, nextProps, transaction);
      // scr: ------------------------------------------------------> 2)
      this._updateDOMChildren(lastProps, nextProps, transaction, context);
    // scr: code that is not applicable this time
    ...
    },
    
    ReactDOMComponent@renderers/dom/shared/ReactDOMComponent.js
    

    1)从旧的虚拟DOM(lastProps)和新创建的虚拟DOM(nextProps)获取props

    2)ReactDOMComponent._updateDOMProperties()检查DOM道具的旧版本和新版本,并调用CSSPropertyOperations.setValueForStyles()来更新DOM(如果不同);

    3)ReactDOMComponent._updateDOMChildren()检查DOM内容(文本,内部HTML)的旧版本和新版本,并调用ReactDOMComponent.updateTextContent()来更新DOM(文本)的内容。

    静态调用栈

    ...                                                            ___
    ReactReconciler.receiveComponent()      <----------------|      |
      |-ReactDOMComponent.receiveComponent()                 |      |
        |-this.updateComponent()                             |      |
          |-this._updateDOMProperties()                      |   diffing
            |-CSSPropertyOperations.setValueForStyles()      |      |
          |-this._updateDOMChildren()                        |      |
            |-this.updateTextContent()                       |      |
            |-recursing children (not the focus this time) --|      |
                                                                   ---
    

    通过观察静态调用堆栈,不难推断出递归的工作方式。

    1)此递归的一次迭代更新了一个虚拟DOM的属性;

    2)ReactDOMComponent.updateDOMChildren()还负责遍历当前虚拟DOM的直接子对象,并为每个子对象调用下一个迭代。

    note that sub DOM recursing is not the focus of this post
    请注意,sub DOM递归不是本文的重点

    我在上面的调用堆栈中折叠了一些方法调用,

    |-ReactReconciler.receiveComponent()
      |-ReactDOMComponent[n].receiveComponent()
        |-this.updateComponent()
    =>
    
    |-ReactDOMComponent[n].updateComponent()
    

    并清晰地调用调用栈:

    ...
    |-ReactDOMComponent[6].updateComponent()
      |-this._updateDOMProperties() // scr: ----> same
      |-this._updateDOMChildren
        |-recursing children (not the focus this time...)
          |-ReactDOMComponent[4].updateComponent()
            |-this._updateDOMProperties() // scr: ----> same
            |-this._updateDOMChildren
              |-recursing children (not the focus this time...)
                |-ReactDOMComponent[2].updateComponent()
                  |-this._updateDOMProperties() // scr: ----> same
                  |-this._updateDOMChildren     // scr: ----> same
                |-ReactDOMComponent[3].updateComponent()
                  |-this._updateDOMProperties() // scr: ----> same
                  |-this._updateDOMChildren     // scr: ----> same
          |-ReactDOMComponent[5].updateComponent()
            |-this._updateDOMProperties()
              |-CSSPropertyOperations.setValueForStyles()
            |-this._updateDOMChildren
              |-this.updateTextContent()
    

    ReactDOMComponent._updateDOMProperties()—检查DOM是否已更改

    This is the overlooked method in {post three *6}In this article we focus on only STYLE updating related code.
    这是{第三篇 * 6}中被忽略的方法。在本文中,我们仅关注STYLE更新相关代码。

    _updateDOMProperties: function(lastProps, nextProps, transaction) {
      var propKey;
      var styleName;
      var styleUpdates;
    // scr: --------------------------------------------------------> 1)
      for (propKey in lastProps) {
        if (
          nextProps.hasOwnProperty(propKey) ||
          !lastProps.hasOwnProperty(propKey) ||
          lastProps[propKey] == null
        ) {
          continue;
        }
        if (propKey === STYLE) {
          var lastStyle = this._previousStyleCopy;
          for (styleName in lastStyle) {
            if (lastStyle.hasOwnProperty(styleName)) {
              styleUpdates = styleUpdates || {};
              styleUpdates[styleName] = '';
            }
          }
          this._previousStyleCopy = null;
        } else if ... {
          // scr: not the focus this time
    ...
        }
      }
    
      // scr: --------------------------------------------------> end 1)
      for (propKey in nextProps) {
        var nextProp = nextProps[propKey];
        var lastProp = propKey === STYLE
            ? this._previousStyleCopy
            : lastProps != null ? lastProps[propKey] : undefined;
        if (
          !nextProps.hasOwnProperty(propKey) ||
          nextProp === lastProp ||
          (nextProp == null && lastProp == null)
        ) {
          continue;
        }
        if (propKey === STYLE) {
          if (nextProp) {
            // scr: DEV code
    ...
           // scr: -------------------------------------------------> 2)
           nextProp = this._previousStyleCopy = Object.assign({}, nextProp);
          } else {
            this._previousStyleCopy = null;
          }
          if (lastProp) { // scr: ----------------------------------> 3)
            // scr: the comment applies here -----------------------> a)
            // Unset styles on `lastProp` but not on `nextProp`. 
                       
            for (styleName in lastProp) {
              if (
                lastProp.hasOwnProperty(styleName) &&
                (!nextProp || !nextProp.hasOwnProperty(styleName))
              ) {
                styleUpdates = styleUpdates || {};
                styleUpdates[styleName] = '';
              }
            }
            // scr: the comment applies here -----------------------> b)
            // Update styles that changed since `lastProp`.
            for (styleName in nextProp) {
              if (
                nextProp.hasOwnProperty(styleName) &&
                lastProp[styleName] !== nextProp[styleName]
              ) {
                styleUpdates = styleUpdates || {};
                styleUpdates[styleName] = nextProp[styleName];
              }
            }
          } else { // scr: -----------------------------------------> 4)
            // Relies on `updateStylesByID` not mutating `styleUpdates`.
            styleUpdates = nextProp;
          }
        } else if (...) {
          // scr: DEV code
    ...
        }
      }
      if (styleUpdates) { // scr: ----------------------------------> 5)
        CSSPropertyOperations.setValueForStyles(
          getNode(this),
          styleUpdates,
          this,
        );
      }
    },
    
    ReactDOMComponent@renderers/dom/shared/ReactDOMComponent.js
    

    1)如果新属性根本不包含“style”

    ...
    if (nextProps.hasOwnProperty(propKey) ||...) {
      continue;
    } // scr: else, do something
    ...
    

    将所有现有样式条目标记为“删除”,请注意,现有样式存储在this._previousStyleCopy中,步骤2)中;

    2)将nextProp(当前样式)复制到this._previousStyleCopy;

    3)如果已有样式,

    var lastProp = propKey === STYLE
            ? this._previousStyleCopy
    ...
    if (lastProp) {
    ...
    

    请通过以下方式进行更新:a)将nextProp中没有的现有样式条目标记为“删除”,b)nextProp中的样式条目与相同键上的现有条目不同,将它们标记为“添加”;

    4)如果没有,只需将nextProp中的所有样式标记为“添加”;

    5)进行真正的DOM操作。 请注意,getNode()ReactDOMComponentTree.getNodeFromInstance()的别名,该别名使用ReactDOMComponent._hostNode来获取关联的DOM元素{Figure-III} {第三篇}。

    ctl-f “ReactDOMComponent[ins]._hostNode”

    CSSPropertyOperations.setValueForStyles()-更新属性

    setValueForStyles: function(node, styles, component) {
      var style = node.style;
      for (var styleName in styles) {
        if (!styles.hasOwnProperty(styleName)) {
          continue;
        }
        // scr: DEV code or code that is not applicable
    ...
    
        if (isCustomProperty) {
    ...
        } else if (styleValue) {
          style[styleName] = styleValue;
        } else {
            code that is not applicable this time
    ...
        }
      }
    },
    
    CSSPropertyOperations@renderers/dom/shared/CSSPropertyOperations.js
    

    这里唯一适用的行是style [styleName] = styleValue; 使用前面方法中标记的style设置node.style

    结果,Node.style ['color'] ='red'

    _updateDOMChildren —检查DOM的内容是否已更改(并递归其子级)

    We omit the dangerouslySetInnerHTML related code and focus only on hot paths
    我们省略了与DangerouslySetInnerHTML相关的代码,仅关注热门路径

    _updateDOMChildren: function(
      lastProps,
      nextProps,
      transaction,
      context
    ) {
      var lastContent = CONTENT_TYPES[typeof lastProps.children]
          ? lastProps.children
          : null;
      var nextContent = CONTENT_TYPES[typeof nextProps.children]
          ? nextProps.children
          : null;
      // scr: code that is not applicable
    ...
    // Note the use of `!=` which checks for null or undefined.
      // scr: used by recursing children, to be continued...
      var lastChildren = lastContent != null ? null : lastProps.children;
      var nextChildren = nextContent != null ? null : nextProps.children;
      // scr: code that is not applicable
    ...
      if (lastChildren != null && nextChildren == null) {
        // scr: recursing children, to be continued...
        this.updateChildren(null, transaction, context);
      } else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
        // scr: DEV code and code that is not applicable
    ...
      }
      if (nextContent != null) {
        if (lastContent !== nextContent) {
          this.updateTextContent('' + nextContent);
          // scr: DEV code
    ...
        }
      } else if (nextHtml != null) {
        // scr: code that is not applicable
    ...
      } else if (nextChildren != null) {
        // scr: DEV code
    ...
        // scr: recursing children, to be continued...
        this.updateChildren(nextChildren, transaction, context);
      }
    },
    
    ReactDOMComponent@renderers/dom/shared/ReactDOMComponent.js
    

    这里唯一适用的行是

    this.updateTextContent(‘’ + nextContent);
    

    ReactDOMComponent.updateTextContent()-更新内容

    大概ReactDOMComponent.updateTextContent()用于将文本从“start”设置为“end”。 但是这个过程的调用栈对于这个简单的操作来说太深了,

    updateTextContent: function(nextContent) {
      var prevChildren = this._renderedChildren;
      // Remove any rendered children. scr: -------> the comment applies
      ReactChildReconciler.unmountChildren(prevChildren, false);
      for (var name in prevChildren) {
        // scr: sanity check
    ...
      }
      
      // Set new text content. scr: ---------------> the comment applies
      var updates = [makeTextContent(nextContent)];
      processQueue(this, updates);
    },
    
    function processQueue(inst, updateQueue) {
      ReactComponentEnvironment.processChildrenUpdates(inst, updateQueue);
    }
    
    ReactMultiChild@renderers/shared/stack/reconciler/ReactMultiChild.js
    

    在这里,ReactComponentBrowserEnvironment作为ReactComponentEnvironment被注入。

    dangerouslyProcessChildrenUpdates: function(parentInst, updates) {
      var node = ReactDOMComponentTree.getNodeFromInstance(parentInst);
      DOMChildrenOperations.processUpdates(node, updates);
    },
    
    ReactDOMIDOperations@renderers/dom/client/ReactDOMIDOperations.js
    

    The ReactDOMComponentTree.getNodeFromInstance() method is discussed in the previous section.
    上一节讨论了ReactDOMComponentTree.getNodeFromInstance()方法。

    processUpdates: function(parentNode, updates) {
      // scr: DEV code
    ...
      for (var k = 0; k < updates.length; k++) {
        var update = updates[k];
        switch (update.type) {
          // scr: code that is not applicable
    ...
          case 'TEXT_CONTENT':
            setTextContent(parentNode, update.content);
            // scr: DEV code
    ...
            break;
    ...
    
    DOMChildrenOperations@renderers/dom/client/utils/DOMChildrenOperations.js
    

    不出所料,此堆栈中的最后一张卡是setTextContent(),它直接设置Node.textContent。 {第五篇}中介绍了此方法,因此我将不再重复其实现。

    ReactDOMComponent.updateTextContent()的子调用堆栈及其“end”结果:

    |-ReactDOMComponent.updateTextContent()
      |-processQueue()
        |-ReactComponentEnvironment.processChildrenUpdates()
        |=ReactDOMIDOperations.dangerouslyProcessChildrenUpdates()
          |-ReactDOMComponentTree.getNodeFromInstance()
          |-DOMChildrenOperations.processUpdates()
            |-setTextContent()
              |-Node.textContent = 'end'
    

    在下一篇文章中,我们将通过观察DOM树的突变来进一步研究差异算法,这也将结束本系列文章(一段时间)。 我希望您下次使用setState()时会感到更多。

    (原文链接)Understanding The React Source Code - UI Updating (Individual DOM) VIII

    (上一篇)【译】了解React源代码-UI更新(事务)7

    (下一篇)【译】了解React源代码-UI更新(DOM树)9

    相关文章

      网友评论

          本文标题:【译】了解React源代码-UI更新(单个DOM)8

          本文链接:https://www.haomeiwen.com/subject/nuxemctx.html