DOM缓存机制

在进行各种操作时,时常涉及到DOM树的查询操作,为了加速这个过程,React设计了一套节点缓存机制,以额外的存储空间换取更高的查询效率。

ReactDOMNodeCache

nodeCache中存储了id和node的键值对。

var nodeCache = {};

/**
 * DOM node cache only intended for use by React. Placed into a shared module so
 * that both read and write utilities may benefit from a shared cache.
 */
var ReactDOMNodeCache = {
  /**
   * Releases fast id lookups (node/style cache). This implementation is
   * aggressive with purging because the bookkeeping associated with doing fine
   * grained deleted from the cache may outweight the benefits of the cache. The
   * heuristic that should be used to purge is 'any time anything is deleted'.
   * Typically this means that a large amount of content is being replaced and
   * several elements would need purging regardless. It's also a time when an
   * application is likely not in the middle of a "smooth operation" (such as
   * animating/scrolling).
   */
  purgeEntireCache: function() {
    nodeCache = {};
    return nodeCache;
  },
  getCachedNodeByID: function(id) {
    return nodeCache[id] ||
      (nodeCache[id] =
        document.getElementById(id) ||
        ReactMount.findReactRenderedDOMNodeSlow(id));
  }
};

当试图根据id查找某个节点时,首先从nodeCahce中查找,如果没有,则根据document.getElementById查找,如果仍然没有,则根据# ReactMount.findReactRenderedDOMNodeSlow(id)

ReactMount.findReactRenderedDOMNodeSlow

如果nodeCache和documen中都不存在对应id的节点,说明有可能是一个React渲染的节点。此时,先通过ReactMount.findReactContainerForID找到包含这个id的React容器,然后再通过ReactInstanceHandles.findComponentRoot在React容器中查找包含id的react组件。

findReactRenderedDOMNodeSlow: function(id) {
    var reactRoot = ReactMount.findReactContainerForID(id);
    return ReactInstanceHandles.findComponentRoot(reactRoot, id);
  }

findReactContainerForID是根据某个给定的id,上溯找到这个id对应的react实例。所有React渲染的节点,其id都遵循特定的格式,ReactRootID的生成代码如下,其中mountPointCount是一个全局计数器:

 getReactRootID: function(mountPointCount) {
    return '.reactRoot[' + mountPointCount + ']';
  },
 findReactContainerForID: function(id) {
    var reatRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id);
    // TODO: Consider throwing if `id` is not a valid React element ID.
    return containersByReactRootID[reatRootID];
  }

containersByReactRootID是一个map,它以id为key,DOM节点为值,可以根据给定的id查询其对应的dom节点,在节点被渲染时,其信息会被注册到containersByReactRootID中,相关代码如下:

registerContainer: function(container) {
    var reactRootID = getReactRootID(container);
    if (reactRootID) {
      // If one exists, make sure it is a valid "reactRoot" ID.
      reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID);
    }
    if (!reactRootID) {
      // No valid "reactRoot" ID found, create one.
      reactRootID = ReactInstanceHandles.getReactRootID(
        globalMountPointCounter++
      );
    }
    containersByReactRootID[reactRootID] = container;
    return reactRootID;
  }

而在找到了根容器节点和带查询的id时,就可以开始在根容器节点中查找这个id对应的内容了,其查询代码如下:

findComponentRoot: function(ancestorNode, id) {
    var child = ancestorNode.firstChild;
    while (child) {
      if (id === child.id) {
        return child;
      } else if (id.indexOf(child.id) === 0) {
        return ReactInstanceHandles.findComponentRoot(child, id);
      }
      child = child.nextSibling;
    }
    // Effectively: return null;
  }

如果根节点的某个子节点id与目标id相等,则返回该节点;如果子节点的id比目标节点更长,但其id的起始部分与目标节点一致,同时还包含了其他后缀,根据React的节点命名规范,该子节点必然是待查询id的根容器节点,此时递归调用findComonentRoot即可。

results matching ""

    No results matching ""