从0到一实现React Fiber从零到一实现React Fiber

embedded/2025/2/28 8:09:49/

为什么引入fiber

JavaScript引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起等待 如果 JavaScript线程长时间地占用了主线程,那么渲染层面的更新就不得不长时间地等待,界面长时间不更新,会导致页面响应度变差,用户可能会感觉到卡顿 而这也正是React 15 的 Stack Reconciler 所面临的问题,当React在渲染组件时,从开始到渲染完成整个过程是一气呵成的,无法中断 如果组件较大,那么js线程会一直执行,然后等到整棵VDOM树计算完成后,才会交给渲染的线程 这就会导致一些用户交互、动画等任务无法立即得到处理,导致卡顿的情况,

JavaScript执行Javascript引擎和页面渲染在同一个线程中,GUI渲染和Javascript执行两者之间是互斥的 工 如果某个任务执行时间过长,浏览器就会推迟渲染。这就引入了Fiber

React Fiber是React 16引入的一种新的协调引擎,它的目标是使React能够更好地处理大型应用和动态更新。Fiber的主要目标是实现以下几个方面的改进:

  1. 增量渲染:将渲染工作分成多个小任务,避免长时间占用主线程,从而提高应用的响应性。
  1. 可中断和恢复:在渲染过程中,可以中断当前的渲染任务,并在稍后恢复,这样可以更好地利用浏览器的主线程。
  1. 优先级调度:根据任务的优先级来调度渲染工作,确保高优先级的任务(如用户交互)能够优先执行。
  1. 并发模式:支持并发模式,使得React可以同时处理多个更新任务,提高渲染效率。

Fiber执行阶段

每次渲染有两个阶段:Reconciliation(协调render阶段)和Commit(提交阶段) - 协调的阶段:可以认为是Diff阶段,这个阶段可以被终止,这个阶段会找出所有节点变更,例如节点新增、删除、属性变更等等,这些变更React称之为副作用。 - 提交阶段:将上一阶段计算出来的需要处理的副作用(effects)一次性执行了。这个阶段必须同步执行,不能被打断。

Fiber的核心概念

Fiber是React中用于表示组件树的数据结构,每个Fiber节点对应一个React组件实例或DOM节点。Fiber节点包含以下关键属性:

  • type :组件的类型(类组件或函数组件)。
  • key :组件的唯一标识。
  • props :组件的属性。
  • stateNode :组件对应的DOM节点或组件实例。
  • return :父Fiber节点。
  • child :第一个子Fiber节点。
  • sibling :下一个兄弟Fiber节点。
  • alternate :当前Fiber节点的旧版本(用于并发模式)。
  • Fiber树在执行时采用的是链表结构,存放指针如上几个,具备了可以随机存取的特性,而且时间复杂度很低,当中断或者优先调度某个Fiber的时候都能很快的找到他的父节点和子节点和兄弟节点,完整渲染的时候是按照树的深度优先遍历的。

手写简单的Render函数

下面是一个简化的React Fiber渲染过程的实现,包括创建Fiber节点和基本的协调过程:

// 定义Fiber节点
class Fiber {constructor(type, key, props, returnFiber) {this.type = type;this.key = key;this.props = props;this.return = returnFiber;this.child = null;this.sibling = null;this.alternate = null;this.stateNode = null;}
}
// 创建Fiber树的函数
function createFiberTree(element, returnFiber) {const fiber = new Fiber(element.type, element.key, element.props, returnFiber);if (typeof element.type === 'function') {// 如果是函数组件,调用它并获取子元素const children = element.type(element.props);reconcileChildren(fiber, children);} else {// 如果是DOM元素,设置stateNodefiber.stateNode = createDom(fiber);}return fiber;
}
// 创建DOM节点的函数
function createDom(fiber) {const dom =fiber.type === 'TEXT_ELEMENT'? document.createTextNode(''): document.createElement(fiber.type);updateDom(dom, {}, fiber.props);return dom;
}
// 更新DOM的函数
const isEvent = key => key.startsWith('on');
const isProperty = key =>key !== 'children' && !isEvent(key) && key !==  'key';
function updateDom(dom, prevProps, nextProps) {// 移除旧的属性Object.keys(prevProps).filter(isProperty).filter(name => !(name in nextProps)).forEach(name => {dom[name] = '';});// 设置新的属性Object.keys(nextProps).filter(isProperty).forEach(name => {dom[name] = nextProps[name];});// 移除旧的事件监听器Object.keys(prevProps).filter(isEvent).forEach(name => {const eventType = name.toLowerCase().substring(2);dom.removeEventListener(eventType, prevProps[name]);});// 添加新的事件监听器Object.keys(nextProps).filter(isEvent).forEach(name => {const eventType = name.toLowerCase().substring(2);dom.addEventListener(eventType, nextProps[name]);});
}
// 协调子元素的函数
function reconcileChildren(wipFiber, elements) {let index = 0;let oldFiber = wipFiber.alternate && wipFiber.alternate.child;let prevSibling = null;while (index < elements.length || oldFiber != null) {const element = elements[index];let newFiber = null;const sameType = oldFiber && element && element.type === oldFiber.type;if (sameType) {newFiber = {type: oldFiber.type,key: oldFiber.key,props: element.props,return: wipFiber,stateNode: oldFiber.stateNode,alternate: oldFiber,};newFiber.child = oldFiber.child;newFiber.sibling = oldFiber.sibling;oldFiber = oldFiber.sibling;} else if (element) {newFiber = createFiberTree(element, wipFiber);} else if (oldFiber) {oldFiber.return.child = null;}if (oldFiber === null) {if (newFiber != null) {if (prevSibling == null) {wipFiber.child = newFiber;} else {prevSibling.sibling = newFiber;}}} else if (newFiber == null) {if (prevSibling == null) {wipFiber.child = null;} else {prevSibling.sibling = null;}}prevSibling = newFiber;index++;}
}
// 简单的render函数
function render(element, container) {const rootFiber = createFiberTree(element, null );const dom = rootFiber.stateNode;container.appendChild(dom);
}
// 示例使用
const element = {type: 'div',props: {children: 'Hello, Fiber!',},
};
render(element, document.getElementById('root'));

总结

以上代码实现了一个简化的React Fiber渲染过程,包括创建Fiber节点、协调子元素和更新DOM。实际的React Fiber实现要复杂得多,涉及到更多的优化和功能,如优先级调度、并发模式等。通过这个简化的示例,可以更好地理解iber的基本概念和工作原理。


http://www.ppmy.cn/embedded/167757.html

相关文章

【做一个微信小程序】校园地图页面实现

前言 上一个教程我们实现了小程序的一些的功能&#xff0c;有背景渐变色&#xff0c;发布功能有的呢&#xff0c;已支持图片上传功能&#xff0c;表情和投票功能开发中&#xff08;请期待&#xff09;。下面是一个更高级的微信小程序实现&#xff0c;包含以下功能&#xff1a;…

【iOS】小蓝书学习(四)

小蓝书学习&#xff08;四&#xff09; 第23条&#xff1a;通过委托与数据源协议进行对象间通信第24条&#xff1a;将类的实现代码分散到便于管理的数个分类之中第25条&#xff1a;总是为第三方类的分类名加前缀第26条&#xff1a;勿在分类中声明属性第27条&#xff1a;使用“c…

深度学习笔记——循环神经网络RNN

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍面试过程中可能遇到的循环神经网络RNN知识点。 热门专栏 机器学习 机器学习笔记合集 深度学习 深度学习笔记合集 文章目录 热门专栏机器学习深度学…

基于SpringBoot的校园消费点评管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

SQLite数据库从0到1

SQLite SQLite基础知识 SQLite数据库功能特性&#xff1a;ACID事务&#xff1b;支持数据库大小至2TB&#xff1b;足够小&#xff0c;大致13万行C代码4MB左右&#xff1b;存储在单一磁盘文件中的完整数据库。独立&#xff0c;无额外依赖。源码完全开源。支持多种编程语言&#…

嵌入式八股文(五)硬件电路篇

一、名词概念 1. 整流和逆变 &#xff08;1&#xff09;整流&#xff1a;整流是将交流电&#xff08;AC&#xff09;转变为直流电&#xff08;DC&#xff09;。常见的整流电路包括单向整流&#xff08;二极管&#xff09;、桥式整流等。 半波整流&#xff1a;只使用交流电的正…

HIVE表操作

Hive有四种表&#xff1a;内部表&#xff0c;外部表&#xff0c;分区表&#xff0c;分桶表。分别对应不同的需求。又可将他们分为两组内部表和外部表、分区表和分桶表&#xff0c;其中分区表在企业中用的最多&#xff0c;可以说百分之八九十的表都是分区表。 创建表 方式1&…

大模型在癫痫预测及临床方案制定中的应用研究报告

目录 一、引言 1.1 研究背景与意义 1.2 国内外研究现状 1.3 研究方法与创新点 二、大模型技术概述 2.1 大模型的基本原理 2.2 常见大模型在医疗领域的应用 2.3 大模型用于癫痫预测的优势 三、大模型在癫痫术前预测中的应用 3.1 致痫灶定位预测 3.1.1 基于影像数据的…