🍂 React “头部”的容器节点

173 阅读2分钟

著有《React 源码》《React 用到的一些算法》《javascript地月星》等多个专栏。欢迎关注。

文章不好写,要是有帮助别忘了点赞,收藏,评论 ~你的鼓励是我继续挖干货的动力🔥。

另外,本文为原创内容,商业转载请联系作者获得授权,非商业转载需注明出处,感谢理解~

前言

上一篇是React Fiber树“头部”的构建,还梳理了“头部”的FiberRootNode HostRootFiber HostRoot。这一篇继续梳理“头部”的容器节点,因为一些东西容易搞混。

  • HostRootFiber和div#root是不是对应的?
  • div#root是不是App的根节点?
  • HostPortal和div#modal-container是不是对应的?

都不是。div#root、div#modal-container是HostRootFiber、HostPortal的容器节点(containerInfo)。 还要补充,div#modal-container有自己另外对应的HostComponent类型的Fiber。div#root没有对应的HostComponent Fiber。

stateNode和containerInfo

每个Fiber节点都有stateNode属性,对于HostComponent(对应着真实DOM)类型的Fiber节点,它们的stateNode保存着真实DOM的索引。

containerInfo是容器的索引,只有HostRoot、HostPortal类型的Fiber节点有容器。

区别是什么?
HostComponent是从DOM创建过来的,进行了属性、事件上的关联。 HostRoot、HostPortal不是从容器创建过来的,没有进行这样的关联。 例如,对于根节点div#root,它刚好落在App的“DOM父节点”,所以容易误解为App的Fiber父节点对应的DOM。

  • HostRootFiber,tag=HostRoot, 是App的父节点
HostRootFiber.child = App;
App.return = HostRootFiber
  • 表面上看都是App的“父节点”,但是HostRoot和div#root不是对应的(不是从div#root创建过来):
graph LR
A["❌ HostRootFiber, tag=HostRoot"]
B["div#root"]

A --stateNode或containerInfo--> B

confinerInfo不是这样用的,这里至少要改成stateNode。
但是同时,div#root不是HostRootFiber对应的DOM实例,所以用stateNode也不对。
都不对。

容器节点 containerInfo

stateNode不全指真实的DOM节点,那仅仅是在HostComponent,这些节点是从DOM创建过来的,当然有DOM,但是函数组件呢,类组件呢,HostRoot呢都没有DOM。

在HostRoot和HostPortal中,都需要一个容器。 stateNode发挥的另一个作用,通过它找到一个对象,在这个对象上找到容器。

  • HostRoot的容器节点div#root
HostRoot.stateNode = FiberRootNode
FiberRootNode.containerInfo = div#root//容器
div#root.$reactFiber... 没有这个属性,div#root没有自己另外的HostComponent Fiber
截屏2025-10-29 下午5.34.54.png
  • HostPortal的容器节点div#modal-container
HostPortal.stateNode = {...containerInfo...}//一个普通的对象,描述HostPortal
{...containerInfo...}.containerInfo = div#modal-container//容器
div#modal-container.__reactFiber$... = Fiber,tag=HostComponent//容器的Fiber,不是HostPortl了,是另一个独立的Fiber,有自己另外的HostComponent Fiber
Fiber,tag=HostComponent.stateNode = div#modal-container

每个DOM上也有一个__reactFiber$...属性关联到它的Fiber(div#root没有)。
可以想象出大概是这么一个关系: HostPortal2.png

从左到右是HostRoot Fiber子树、包含HostPortal的Fiber树、包含HostPortal的DOM树。
蓝色是Fiber实例,黄色是普通对象,红色是DOM实例。
ReactDOM.createPortal(<div>具体内容</div>,document.getElementById('可以指定任意位置子树作为容器'))。 图里面的Fiber子树、DOM子树看起来像失联了,其实是有联系的,不是游离的,因为Fiber子树、DOM子树不知道会在哪个节点下面,可能在body下,可能在div下,所以没有画上去。

关联阅读:

源码

function createFiberFromPortal(portal, mode, lanes) {
  var pendingProps = portal.children !== null ? portal.children : [];
  var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
  fiber.lanes = lanes;
  fiber.stateNode = {
    containerInfo: portal.containerInfo,
    pendingChildren: null,
    // Used by persistent updates
    implementation: portal.implementation
  };
  return fiber;
} // Used for stashing WIP properties to replay failed work in DEV.



// 从mountIndeterminateComponent renderWithHooks创建
// portal: {
//   $$typeof: Symbol(react.portal)
//   children: {$$typeof: Symbol(react.element), type: 'div', key: null, ref: null, props: {…}, …}
//   containerInfo: div#modal-container
//   implementation: null
//   key: null
//   [[Prototype]]: Object
// }
function updatePortal(returnFiber, current, portal, lanes) {
  if (current === null || current.tag !== HostPortal || current.stateNode.containerInfo !== portal.containerInfo || current.stateNode.implementation !== portal.implementation) {
    // Insert
    var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
    created.return = returnFiber;
    return created;
  } else {
    // Update
    var existing = useFiber(current, portal.children || []);
    existing.return = returnFiber;
    return existing;
  }
}

结语

可以简单理解,stateNode发挥着2个作用,1.在HostComponent中指向DOM实例。2.在HostRoot,HostPortal中指向一个 包含containerInfo属性的对象,containerInfo指向容器。