皮皮网
皮皮网

【妙笔阁源码】【atm源码】【dsyexpress源码】react源码reconciler

来源:投影互动系统源码 发表时间:2024-12-24 10:01:27

1.[译] 如何自定义React渲染器-Hello World
2.React Fiber原理?
3.React设计原理,由浅入深解析 react18 源码(一)

react源码reconciler

[译] 如何自定义React渲染器-Hello World

       本文详细阐述了如何创建一个基本的自定义React渲染器,并通过Hello World示例进行演示。首先,创建一个React项目,并在其中实现一个极简的妙笔阁源码DOM渲染器。接着,为了展示自定义渲染器的使用,将实现一个简单的计数器组件,并在其中替换默认的ReactDOM渲染器。

       在App.js文件中添加计数器组件,展示基本功能。

       在index.js文件中,使用自定义的渲染器MyCustomRenderer替换默认的ReactDOM渲染器。确保引入react-reconciler包,通过yarn add react-reconciler完成安装。

       创建myCustomRenderer.js文件,实现渲染器的核心功能,主要为render函数,使用react-reconciler作为支持。

       在index.js中,使用MyCustomRenderer.render方法替换ReactDOM.render,接收三个参数:ReactElement、domElement和回调函数,其中回调函数在本示例中被忽略。

       引入react-reconciler包后,需配置hostConfig,atm源码包含回调函数,如getRootHostContext、supportsMutation等。其中,getRootHostContext函数作为hostConfig的一部分,用于维护实现renderer所需信息。

       根据错误提示,补充hostConfig中空方法,如支持UI树操作、UI更新、prop更新等。

       完成以上步骤后,即可实现一个基本的自定义React渲染器,满足Hello World级别的应用需求。该自定义渲染器简单实用,为后续复杂应用的渲染提供基础框架。完整源码可于GitHub仓库查看。

React Fiber原理?

       Fiber的特点/作用

       Fiber能够使得动画、布局和页面交互变得更加的流畅。

       一:Fiber的概念

         React Fiber是react执行渲染时的一种新的调度策略,JavaScript是单线程的,一旦组件开始更新,主线程就一直被React控制,这个时候如果再次执行交互操作,就会卡顿。

         React Fiber就是dsyexpress源码通过对象记录组件上需要做或者已经完成的更新,一个组件可以对应多个Fiber。

         在render函数中创建的React Element树在第一次渲染的时候会创建一颗结构一模一样的的Fiber节点树。不同的React Element类型对应不同的Fiber节点类型。一个React Element的工作就由它对应的Fiber节点来负责。

         一个React Element可以对应不止一个Fiber,因为Fiber在update的时候,会从原来的Fiber(我们称为current)clone出一个新的Fiber(我们称之为alternate)。俩个Fiber diff出的变化(side effect)记录在alternate上。所以一个组件在更新时最多会有俩个Fiber与其对应,在更新结束后alternate会取代之前的current称为新的current节点。

         React Fiber重构这种方式,渲染过程采用切片的方式,每执行一会儿,就歇一会儿。如果有优先级更高的任务到来以后呢,就会先去执行,降低页面发生卡顿的可能性,使得React对动画等实时性要求较高的场景体验更好。

       二:什么是Fiber?

         当js在处理大型计算的时候会导致页面出现卡帧的现象,更严重的会出现页面“假死”。所以在这些情况下,必然会导致动画丢帧、不连贯,用户体验就特别差。为了解决这个问题,我们可以将大型的pdfescape源码计算拆分成一个个小型计算,然后按照执行顺序异步调用,这样就不会长时间霸占线程,UI也能在俩次小型计算的执行间隙进行更新,从而给与用户及时的反馈,Fiber就是这样做的,并且以一种更高逼格的方式实现了。

       Driving Idea

         如果说v.0之前的React解决了HOW(如何用最少的DOM操作成本来update视图)的问题,那么这一次Fiber的出现,在这个基础上还解决了WHEN(何时update视图的哪一部分)的问题。

          分片优先级!!!

         基于上述这些原因,Fiber实现了一个虚拟调用栈,并给所有的update进行优先级排序,如下:

       'use strict';

       export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5;

       module.exports = {

       NoWork: 0, // No work is pending.

       SynchronousPriority: 1, // 用于控制文本输入。同步的副作用.

       AnimationPriority: 2, //需要在下一帧之前完成.

       HighPriority: 3, // 需要很快完成的互动才能产生反应.

       LowPriority: 4, // 数据获取,或更新存储的结果.

       OffscreenPriority: 5, // 将不可见,但做的工作,以防它成为可见.

       };

         然后根据这些update的优先级,来决定执行的顺序。

         我们可以看到动画和页面交互都是优先级比较高的,这也是Fiber能够使得动画、布局和页面交互变得更加的流畅的原因之一。

         可以把Priority分为同步和异步两个类别,同步优先级的反手源码任务会在当前帧完成,包括SynchronousPriority和TaskPriority。异步优先级的任务则可能在接下来的几个帧中被完成,包括HighPriority、LowPriority以及OffscreenPriority。

         React v.3.2的优先级,不再这么划分,分为三类:NoWork、sync、async,前两类可以认为是同步任务,需要在当前tick完成,过期时间为null,最后一类异步任务会计算一个。

         expirationTime,在workLoop中,根据过期时间来判断是否进行下一个分片任务,scheduleWork中更新任务优先级,也就是更新这个expirationTime。至于这个时间怎么计算,可以查看源码。

       三:Fiber的基本原则:

         更新任务分成俩个阶段,Reconcilition Phase(调和阶段)和Commit Phase(交付阶段)。Reconciliation Phase的任务干的事情是,找出要做的更新工作(Diff Fiber Tree),就是一个计算阶段,计算结果可以被缓存,也就可以被打断;Commit Phase需要提交所有更新并渲染,为了防止页面抖动,被设置为不能打断。

         PS:componentWillMount

         omponentWillReceiveProps componentWillUpdate 几个生命周期方法,在Reconciliation Phase被调用,有被打断的可能(时间用尽等情况),所以可能被多次调用。其实shouldComponentUpdate 也可能被多次调用,只是它只返回true或者false,没有副作用,可以暂时忽略。

       四:Fiber的数据结构

         fiber是个链表,有child和sibing属性,指向第一个子节点和相邻的兄弟节点,从而构成fiber tree。return 属性指向其父节点。

         更新队列,updateQueue,是一个链表,有first和last俩个属性,指向第一个和最后一个update对象。

         每个fiber有一个属性updateQueue指向其对应的更新队列。

         每个fiber(当前fiber可以称为current)有一个属性alternate,开始时指向一个自己的clone体,update的变化会先更新到alternate上,当更新完毕,alternate替换current。

       五:Fiber的执行流程

       用户操作引起setState被调用以后,先调用enqueueSetState方法,该方法可以划分成俩个阶段(个人理解),第一阶段Data Preparation,是初始化一些数据结构,比如fiber,updateQueue,update。

       新的update会通过insertUpdateIntoQueue方法,根据优先级插入到队列的对应位置,ensureUpdateQueues方法初始化俩个更新队列,queue1和current.updateQueue对应,queue2和current.alternate.updateQueue对应。

       第二阶段,Fiber Reconciler,就开始进行任务分片调度,scheduleWork首先更新每个fiber的优先级,这里并没有updatePriority这个方法,但是干了这件事。当fiber.return === null,找到父节点,把所有diff出的变化(side effect)归结到root上。

       requestWork,首先把当前的更新添加到schedule list中(addRootToSchedule),然后根据当前是否为异步渲染(isAsync参数),异步渲染调用。scheduleCallbackWithExpriation方法,下一步高能!!

       scheduleCallbackWithExpriation这个方法在不同环境,实现不一样,chrome等浏览器中使用requestIdleCallback API,没有这个API的浏览器中,通过requestAnimationFrame模拟一个requestIdCallback,来在浏览器空闲时,完成下一个分片的工作,注意,这个函数会传入一个expirationTime,超过这个时间活没干完,就放弃了。

       执行到performWorkOnRoot,就是fiber文档中提到的Commit Phase和Reconciliation Phase俩阶段。

       第一阶段Reconciliation Phase,在workLoop中,通过一个while循环,完成每个分片任务。

       performUnitOfWork也可以分成俩阶段,蓝色框表示。beginWork是一个入口函数,根据workInProgress的类型去实例化不同的react element class。workInProgress是通过alternate挂载一些新属性获得的。

       实例化不同的react element class时候会调用和will有关的生命周期方法。

       completeUnitOfWork是进行一些收尾工作,diff完一个节点以后,更新props和调用生命周期方法等。

       然后进入Commit Phase阶段,这个阶段不能被打断。

       六:Fiber对开发者有什么影响?

       componentWillMount,componentWillReceiveProps,componentWillUpdate几个生命周期方法不再安全,由于任务执行过程可以被打断,这几个生命周期可能会执行多次,如果它们包含副作用(比如Ajax),会有意想不到的bug。React团队提供了替换的生命周期方法。建议如果使用以上方法,尽量使用纯函数,避免以后踩坑。

       需要关注react为任务片设置的优先级,特别是页面用动画的情况。

       如果一直有更高的级别任务,那么fiber算法会先执行级别更高的任务,执行完毕后再通过callback回到之前渲染到一半的组件,从头开始渲染。(看起来放弃已经渲染完的生命周期,会有点不合理,反而会增加渲染时长,但是react确实是这么干的)

React设计原理,由浅入深解析 react 源码(一)

       React设计原理详解:深入理解React 源码(一)

       React的核心工具之一是jsx,它是一种语法扩展,开发者编写的代码会被Babel编译成ReactElement,进一步转化为FiberNode,这是一种虚拟DOM在React中的实现,它能表达组件状态和节点关系,同时具备可扩展性。

       FiberNode的工作方式采用深度优先遍历(DFS)策略,递归地处理ReactElement。在渲染过程中,递归分为beginWork(开始工作)和completeWork(完成工作)两个阶段。在ReactDOM的createRoot和render方法中,scheduleUpdateOnFiber和processUpdateQueue负责更新和创建子fiber节点。

       在commit阶段,关键步骤包括执行root上的mutation,以及对Host类型的FiberNode构建离屏DOM树。ChildReconciler的两个关键点是子ReactElement到子fiber的创建方式和flag标识的设置。最后,学习者需要注意的是,通过阅读本文,可以关注以下三点:

       理解jsx与FiberNode的关系

       掌握React的递归渲染过程和commit阶段的子阶段

       反思和分享你的学习体验,一起探讨React的深入知识

       如果你觉得这篇文章有价值,别忘了在留言区分享你的见解,或者将其推荐给你的朋友。让我们一起深化对React 源码的理解。

相关栏目:时尚