React 之 简易实现 Fiber架构

本文探讨了React的Fiber架构,解释了它如何解决渲染性能问题。通过将递归diff转换为可中断的任务,Fiber提高了浏览器渲染的流畅性。文章介绍了Fiber的核心思想,包括任务拆分、协调和Reconciliation阶段,并给出了简单的Fiber实现示例。此外,还讨论了如何使用useState管理状态更新。
摘要由CSDN通过智能技术生成


此篇文章是在学习一步一步实现 fiber架构的同时,从另外一个由总到分的角度来总结 fiber架构的实现思路。文章末尾有一些学习参考文章可以借鉴。

fiber架构是什么?它解决了什么问题?

当我们项目过于复杂,渲染树过于庞大的时候,那么我们的递归渲染会耗时很长,而且很难被中断,fiber 的主要原理就是让我们在 diff 的过程中可以被中断,去处理更高优先级的事件如:用户事件或者动画,这样让浏览器的渲染更加流畅。

fiber 的核心思想,实现 fiber 我们需要做到什么?如何做?

React 16.0 之前,我们在渲染的过程中,通过去遍历一整棵 虚拟 dom 树来更新变化,我们很难中断,并且无法标记中断来持续工作。那么 fiber 架构将递归 diff 拆分成一个一个小任务,并且随时可中断,利用浏览器的空闲时间来执行,当处理完更高优先级任务后回到中断点继续执行;
要实现这样的机制,fiber做了什么,这里我总结了几点:

  1. 将原有的 vdom 树结构变成一个新的链表结构的树,每个节点都标记了它的(child,sibling,return,分别代表节点的第一个字节点、兄弟节点、父节点),这样可以随时中断,下次从中断处继续执行;
  2. fiber的核心思想是将一个庞大的任务拆分成一个个小的任务块,利用浏览器的空闲时间来执行,那么如何拆分,如何协调?React虚拟dom节点为维度对任务进行拆分,即一个虚拟dom节点对应一个任务,采用深度优先遍历的规则进行协调;
  3. 从根节点开始调度和渲染的过程可以分为两个阶段: render(),和 commit(); render 阶段会根据v-dom找出所有节点的变更(增删更新),然后构建出一棵Fiber 树,这个阶段是可以中断的。commit阶段就是将构建的Fiber three渲染成真实的dom, 这个阶段是不可中断的。
  4. 这里我们通过window.requestIdleCallback 来实现浏览器空闲时执行低优先级任务。

所以说 React Fiber 其实就是通过遍历将 VDom 转换成了 Fiber three,其中每个 Fiber都具有 child、singling、return属性;
遍历遵循深度优先遍历,自上而下,自左向右;从根节点出发,找到他的第一个子元素,找到则返回,没有则找他的兄弟元素,如果无兄弟元素,则直接返回其父元素, 父 ——> 第一个子 ——> 兄弟 ——> 父亲;
在遍历生成Fiber three 的时候根据节点的变更收集 effect list, 通过tag(UPDATE、DELETE、PLACEMENT),直到没有下一个任务,commit 到DOM树上。
在此之前,我们的 vdom 是一颗树,它在 diff 的过程中是没法中断的,于是将其改造成一个链表结构,之前是只有 children 进行递归遍历,现在是包含了父——>子, 子——> 父, 子——> 兄弟这几层关系的链表。

Fiber reconcile

至此,我们可以跟着思路来实现 fiber, 说到 fiber, 它其实就是一个具有各种标识的对象,如:

{
dom: null, // 真实dom,这里function 组件的dom是null
type,
props,
child,
return,
sibling,
alternate: null, // 旧值,用于比对更新
effectTag: 'PLACEMENT'
}

正题来了,首先我们还是来实现createElement(type, config, ...children) 最终返回 虚拟dom 树,这里不是重点,所以不过多介绍,详细可以查看createElement原理,直接贴代码:

/**
 * jsx语法糖,接受三个参数,返回v-dom(js对象)
 * @param {*} type 元素类型:native HTML | Function | Class
 * @param {*} config 属性
 * @param  {...any} children 子元素
 * @returns v-dom 对象
 */
function createElement(type, config, ...children) {
  delete config.__self
  delete config.__source
  const { key, ref, ...rest } = config
  const vdom = {
    $$typeof: Symbol('react.element'),
    type,
    props: {
      ...rest,
      children: children.map(c => typeof c === 'object' ? c : createTextNode(c))
    }
  }
  return vdom
}
/**
 * 创建文本节点对象
 * @param {*} nodeValue 文本值
 * @returns v-dom 对象
 */
function createTextNode(nodeValue) {
  return {
    type: 'TEXT',
    props: {
      nodeValue,
      children: []
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值