Web前端之路

React基础渲染机制

2019-09-29  本文已影响0人  皮神雷卡丘

ReactDOM.js

var ReactDOM = {
  findDOMNode: findDOMNode,
  render: ReactMount.render,
  unmountComponentAtNode: ReactMount.unmountComponentAtNode,
  version: ReactVersion,

  /* eslint-disable camelcase */
  unstable_batchedUpdates: ReactUpdates.batchedUpdates,
  unstable_renderSubtreeIntoContainer: renderSubtreeIntoContainer
  /* eslint-enable camelcase */
};

ReactDOM.render()

ReactDOM.render()方法来自ReactMount文件的render方法:

render: function (nextElement, container, callback) {
    return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
  },

Render()将子树nextElement注入到指定的container中,并调用其回调方法_renderSubtreeIntoContainer,它将完成
(1)React.createElement 生成待插入节点的虚拟DOM实例对象
(2)对新旧虚拟DOM进行对比,找到需要更新的地方批量改动。初次渲染直接将虚拟DOM转换为真实DOM
(3)将DOM插入到container中

Diff 算法

简单mark一下(详细了解需要去看专门的文档):
1.不同的节点类型 直接删去旧的节点,新建一个新的节点。
2.相同节点类型 如果没有key对比它们的属性,只改变需要改变的属性,如果配置了key将作为唯一的节点不是改变其属性,而是直接操作此节点。

_renderSubtreeIntoContainer

_renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
  /*
  *将callback放入到React的更新队列中,判断nextElement有效性以及当前是发开环境还是生产环境。(代码省略)
  */
  …
  var nextWrappedElement = React.createElement(TopLevelWrapper, { child: nextElement });
  // 返回一个ReactElement实例对象,也就是虚拟DOM的实例对象,下面就要把它渲染出来
  var nextContext;
  /*(1)判断是否有parentComponent,如果有则将其写到parentComponent*/
  if (parentComponent) {
    var parentInst = ReactInstanceMap.get(parentComponent);
    nextContext = parentInst._processChildContext(parentInst._context);
  } else {
    nextContext = emptyObject;
  }
  var prevComponent = getTopLevelWrapperInContainer(container);
  /*
 *(2)判断是否有prevComponent,如果有则取其child,利用Diff算法判断是否需要更新,如果需要则调用_updateRootComponen 方法,并return结果。对于初次渲染不需要该过程。 
 */
  if (prevComponent) {
    var prevWrappedElement = prevComponent._currentElement;
    var prevElement = prevWrappedElement.props.child;
    // shouldUpdateReactComponent方法判断是否需要更新,对同一DOM进行比较,type相同,key(如果有)相同的组件做DOM diff 
    if (shouldUpdateReactComponent(prevElement, nextElement)) {
      var publicInst = prevComponent._renderedComponent.getPublicInstance();
      var updatedCallback = callback && function () {
        callback.call(publicInst);
      };
      ReactMount._updateRootComponent(prevComponent, nextWrappedElement, nextContext, container, updatedCallback);
      return publicInst;
    } else {
      ReactMount.unmountComponentAtNode(container);
    }
  }
  /*
  *(3)对于prevElement为null直接跳到此步
  var reactRootElement = getReactRootElementInContainer(container);
  var containerHasReactMarkup = reactRootElement && !!internalGetID(reactRootElement);
  var containerHasNonRootReactChild = hasNonRootReactChild(container);
  var shouldReuseMarkup = containerHasReactMarkup && !prevComponent && !containerHasNonRootReactChild;
  /*
  *3核心部分,调用_renderNewRootComponent,_renderedComponent,getPublicInstance三个方法,
  */
  var component = ReactMount._renderNewRootComponent(nextWrappedElement, container, shouldReuseMarkup, nextContext)._renderedComponent.getPublicInstance();
  if (callback) {
    callback.call(component);
  }
  return component;
}
上一篇下一篇

猜你喜欢

热点阅读