一文掌握React 组件树遍历技巧

 更新时间:2023年04月17日 09:56:07   作者:uccs  
这篇文章主要为大家介绍了React 组件树遍历技巧的掌握,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

本文对应的 react 版本是 18.2.0

下面的 dom 结构react 内部是如何遍历的

const App = () => {
  return (
    <div>
      <button>+1</button>
      <A count={0} />
    </div>
  );
};
const A = (props) => {
  useEffect(() => {
    console.log(props.count);
  }, [props.count]);
  return <div>{props.count}</div>;
};

react 内部遍历核心逻辑:

  • render 时调用 commitPassiveUnmountOnFiber 函数
  • commitPassiveUnmountOnFiber 处理不同的 WorkTag,并调用 recursivelyTraversePassiveUnmountEffects
  • recursivelyTraversePassiveUnmountEffects 根据当前 Fiber 的子节点有没有 passive effectuseEffectuseLayoutEffect)来决定是否遍历当前 Fiber 的子节点
    • 如果子节点有 passive effect,则优先遍历子节点 (深度优先),直到找到最终的叶子节点,退出当前循环
    • 然后进入兄弟节点,开始遍历兄弟节点的子节点
      • 具体从哪个兄弟节点开始遍历,react 选择的是离退出循环的那个叶子节点的父节点,检查有没有子节点,以此循环遍历
    • 直到最后找到所有有 passive effect 的节点

代码简化:

commitPassiveUnmountOnFiber(root.current);
function commitPassiveUnmountOnFiber(finishedWork) {
  // 省略了处理不同的 WorkTag
  recursivelyTraversePassiveUnmountEffects(finishedWork);
}
function recursivelyTraversePassiveUnmountEffects(parentFiber) {
  // 省略了其他处理
  if (parentFiber.subtreeFlags & PassiveMask) {
    let child = parentFiber.child;
    while (child !== null) {
      commitPassiveUnmountOnFiber(child);
      child = child.sibling;
    }
  }
}

所以对于这段 dom 的遍历逻辑是:

  • 首先从根组件开始 FiberRootNode,取到 current
    • 也就是说 FiberRootNode.currentdiv#root 这是一个 fiber,它的 tag3
  • 由于 App 的子组件有 passive effect,所以会进入 App 组件,它的 tag0
  • App 组件中节点是 <div><di >tag5
    • <div> 下面有两个子元素 <button><A>
  • 先遍历 <button> 它的 tag5
  • <button> 内部只有一个文本节点,没有 passive effect
    • 所以 react 不遍历了(跳出当前遍历的循环,也就是 button 这条不在遍历了)
  • 跳出循环后,查看 button 的兄弟节点,它的兄弟节点是 <A><A>tag0
  • 由于 <A> 节点的子节点没有 passive effect,所以跳出循环,结束整个遍历

总结

  • 从跟节点开始遍历
  • 当前组件的子组件有没有 passive effect
  • 采取深度优先
  • 如果 dom 节点内有函数组件,则这个 dom 会被遍历,否则不会遍历
  • 如果当前 fiber 下的所有子 fiber 都没有 passive effect ,则这一整个都链表都不会被遍历
  • 如果当前 fiber 只有 dom,则这些 dom 也不会遍历

总的来说组件会不会别遍历看 fiber 有没有 passive effect

  • 有,一定会被遍历
  • 没有,下面两种情况会被遍历,其他情况不会被遍历
    • passive effect 的父组件
    • passive effect 组件是兄弟组件

passive effect 指的是 useEffectuseLayoutEffect

遍历逻辑如下图所示

图中画绿色勾的都会被遍历,红色勾是遍历的顺序

以上就是一文掌握React 组件树遍历技巧的详细内容,更多关于React 组件树遍历的资料请关注脚本之家其它相关文章!

相关文章

  • 对react中间件的理解

    对react中间件的理解

    这篇文章主要介绍了对react中间件的理解,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 关于React动态修改元素样式的三种方式

    关于React动态修改元素样式的三种方式

    这篇文章主要介绍了关于React动态修改元素样式的三种方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • 详解一个基于react+webpack的多页面应用配置

    详解一个基于react+webpack的多页面应用配置

    这篇文章主要介绍了详解一个基于react+webpack的多页面应用配置,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • 关于React16.0的componentDidCatch方法解读

    关于React16.0的componentDidCatch方法解读

    这篇文章主要介绍了关于React16.0的componentDidCatch方法解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • React学习笔记之列表渲染示例详解

    React学习笔记之列表渲染示例详解

    最近在学习React,学习到了列表渲染这一块,发现网上这方面的资料较少,所以自己来总结下,下面这篇文章主要给大家介绍了关于React学习笔记之列表渲染的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-08-08
  • Input标签自动校验功能去除实现

    Input标签自动校验功能去除实现

    这篇文章主要为大家介绍了Input标签的自动拼写检查功能去除实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • React中的Refs属性你来了解吗

    React中的Refs属性你来了解吗

    这篇文章主要为大家详细介绍了的React Refs属性,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • react路由v6版本NavLink的两个小坑及解决

    react路由v6版本NavLink的两个小坑及解决

    这篇文章主要介绍了react路由v6版本NavLink的两个小坑及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • react路由守卫的实现(路由拦截)

    react路由守卫的实现(路由拦截)

    react不同于vue,通过在路由里设置meta元字符实现路由拦截。本文就详细的介绍一下,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • React渲染机制及相关优化方案

    React渲染机制及相关优化方案

    这篇文章主要介绍了react中的渲染机制以及相关的优化方案,内容包括react渲染步骤、concurrent机制以及产生作用的机会,简单模拟实现 concurrent mode,基于作业调度优先级的思路进行项目优化的两个hooks,感兴趣的小伙伴跟着小编一起来看看吧
    2023-07-07

最新评论