美文网首页React-平凡之路
在react中,虚拟dom是如何转化为fiber链表的?

在react中,虚拟dom是如何转化为fiber链表的?

作者: 修齐治平zzr | 来源:发表于2022-08-04 11:15 被阅读0次
大家都知道jsx是一种js方言,在react中,它能更方便我们的在使用js去创建dom。例如:
let element = (
  <div id="A1">
    A1
    <div id="B1">
    B1
    <div id="C1">C1</div>
    <div id="C2">C2</div>
    </div>
    <div id="B2">B2</div>
  </div>
)
经过了babel编译后会生成这样子一个js对象,它能描述出当前jsx的属性(可以复制去https://www.json.cn/)去格式化下。
{"type":"div","props":{"id":"A1","children":[{"props":{"text":"A1","children":[]}},
{"type":"div","props":{"id":"B1","children":[{"props":{"text":"B1","children":[]}},
{"type":"div","props":{"id":"C1","children":[{"props":{"text":"C1","children":[]}}]}},
{"type":"div","props":{"id":"C2","children":[{"props":{"text":"C2","children":[]}}]}}]}},
{"type":"div","props":{"id":"B2","children":[{"props":{"text":"B2","children":[]}}]}}]}}
那我们需要怎么把它转化成fiber数据类型呢?let's go!
在react的js入口文件,都会有把虚拟dom通过ReactDom.render方法渲染到原生html上
ReactDom.render(Element, document.getElementById('root'));

在正式
创建文件ReactDom.js

function render(element, container) {
    let rootFiber = { // 根fiber
        stateNode: container,
        props: { children: [element] }
    }
    schduleRoot(rootFiber)
}

let nextUnitOfWork = null

function schduleRoot(rootFiber) {
  nextUnitOfWork = rootFiber
}

function workLoop(deadline) { // 实现可中断、恢复
  let shouleYield = false
  while(nextUnitOfWork && !shouleYield) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
    shouldYield = deadline.timeRemaining() < 1 // 如果剩余时间小于1,则继续往下走
  }
  requestIdleCallback(workLoop, { timeout: 500 })
}

function performUnitOfWork(currentFiber) {
  let newChildren = currentFiber.props.children
  reconcileChildren(currentFiber, newChildren) // 把子节点转成fiber结构
  if(currentFiber.child) {
       return currentFiber.child
   }
  while(currentFiber) {
        if(currentFiber.sibling) {
            return currentFiber.sibling
        }
        currentFiber = currentFiber.return
    }
}

function reconcileChildren(currentFiber, newChildren) {
  let newChildrenIndex = 0;
  let prevSibling // 上一个兄弟节点
  while(newChildrenIndex < newChildren.length) {
    let newChild = newChildren[newChildrenIndex] // 第x项
    let newFiber = {
      type: newChild.type,
      props: newChild.props,
      return: currentFiber,
    }
    if(newChildrenIndex === 0) { // 如果是大儿子的话,就让它成为child
      currentFiber.child = newFiber
    } else { // 如果不是大儿子,那就让它成为它哥哥的sibling
      prevSibling.sibling = newFiber
    }
    prevSibling = newFiber
    newChildrenIndex++
  }
}

requestIdleCallback(workLoop, { timeout: 500 })

const ReactDom = {
    render
}
这样一通操作下来虚拟dom对象,就转成了fiber对象,并且给它转成单链表的。实际上react的fiber对象是远远多与上面写的。像这个样子:
{
    // 实例属性
   tag: tag,  // 标记不同组件类型,如函数组件、类组件、文本、原生组件...
   key: key, // react 元素上的 key 就是 jsx 上写的那个 key ,也就是最终 ReactElement 上的
   elementType: null,  // createElement的第一个参数,ReactElement 上的 type
   type: null, // 表示fiber的真实类型 ,elementType 基本一样,在使用了懒加载之类的功能时可能会不一样
   stateNode: null, // 实例对象,比如 class 组件 new 完后就挂载在这个属性上面,如果是RootFiber,那么它上面挂的是 FiberRoot,如果是原生节点就是 dom 对象
    // fiber
   return: null, // 父节点,指向上一个 fiber
   child: null, // 子节点,指向自身下面的第一个 fiber
   sibling: null, // 兄弟组件, 指向一个兄弟节点
   index: 0, //  一般如果没有兄弟节点的话是0 当某个父节点下的子节点是数组类型的时候会给每个子节点一个 index,index 和 key 要一起做 diff
   ref: null, // reactElement 上的 ref 属性
   pendingProps: pendingProps, // 新的 props
   memoizedProps: null, // 旧的 props
   updateQueue: null, // fiber 上的更新队列执行一次 setState 就会往这个属性上挂一个新的更新, 每条更新最终会形成一个链表结构,最后做批量更新
   memoizedState: null, // 对应  memoizedProps,上次渲染的 state,相当于当前的 state,理解成 prev 和 next 的关系
   mode: mode, // 表示当前组件下的子组件的渲染方式
    // effects
   effectTag: NoEffect, // 表示当前 fiber 要进行何种更新(更新、删除等)
   nextEffect: null, // 指向下个需要更新的fiber
   firstEffect: null, // 指向所有子节点里,需要更新的 fiber 里的第一个
   lastEffect: null, // 指向所有子节点中需要更新的 fiber 的最后一个
   expirationTime: NoWork, // 过期时间,代表任务在未来的哪个时间点应该被完成
   childExpirationTime: NoWork, // child 过期时间
   alternate: null,// current 树和 workInprogress 树之间的相互引用
}
我们在本文简化了与虚拟dom转fiber链表无关的边角,只保留了return、child、sibling等指向性节点。因为在在react中,fiber链表是怎么遍历的?直接去遍历fiber链表是挺奇怪的,所以本文算是姊妹篇。

资料参考: 从零实现React16 Fiber架构与Hooks源码

相关文章

  • 在react中,虚拟dom是如何转化为fiber链表的?

    大家都知道jsx是一种js方言,在react中,它能更方便我们的在使用js去创建dom。例如: 经过了babel编...

  • react fiber学习

    什么是react fiber(更新中) fiber是react 16中新的协调引擎,它的主要目标是增量渲染虚拟do...

  • DOM-DIFF

    在 React17+ 中 DOM-DIFF 就是根据老的 fiber 树和最新的 JSX 对比生成新的 fiber...

  • 读react fiber随想

    fiber就是用链表的形式去表示了整个react virtual DOM树. 是之前递归栈形式的另外一种表达. 以...

  • React之JSX

    什么是JSX React的核心机制之一就是虚拟DOM:可以在内存中创建虚拟的DOM元素。React利用虚拟DOM来...

  • React深入6-源码2(React scheduler)

    写在前面 react fiber通过链表,将任务碎片化了,在上一篇博客React fiber[https://gi...

  • React 虚拟DOM详解

    什么是虚拟DOM react 中的 virtual DOM (虚拟DOM),其实就是JS对象。 众所周知,浏览器的...

  • react jsx 渲染流程

    React.render() 编译 createElement 转化为虚拟 DOM render() 函数转化成真...

  • React深入5-源码1(React Fiber)

    React 深入5(React Fiber) 1.传统Dom渲染image.png 2.reconciliaton...

  • 在react中,fiber链表是怎么遍历的?

    Fiber是对react核心算法的重构,react16以上版本引入了fiber架构,其中的设计思想很值得我们去学习...

网友评论

    本文标题:在react中,虚拟dom是如何转化为fiber链表的?

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