成都创新互联是一家专注于做网站、网站制作与策划设计,新泰网站建设哪家好?成都创新互联做网站,专注于网站建设10余年,网设计领域的专业建站公司;建站业务涵盖:新泰等地区。新泰做网站价格咨询:028-86922220
Reconciler 是 React 核心逻辑所在的模块,中文名叫协调器。
在React中,Reconciler(协调器)是负责管理虚拟DOM树更新的关键部分。当组件状态或属性发生更改时,Reconciler的任务是确定如何有效地更新DOM来反映这些更改。这个过程通常被称为 "协调"(Reconciliation)。
Reconciler的核心思想是通过将新的虚拟DOM树与旧的虚拟DOM树进行比较,找出需要实际更新的部分,然后最小化实际DOM操作的数量。这个过程被称为"diffing"算法。
在传统的库(jQuery)工作原理(过程驱动)。
在传统的前端开发中使用的jQuery库的工作原理主要是通过一个简化和统一的API,使得开发者能够更容易地操作DOM、处理事件、创建动画以及发起AJAX请求等。而不是描述UI的状态。所以jQuery的工作原理是过程驱动的。
现代的前端框架结构与工作原理(状态驱动)。
现代的前端框架结构与工作原理
现代框架都需要“编译”这一步骤,用于:
“编译”可以选择两个时机执行:
大部分采用模板语法描述UI的前端框架都会进行AOT优化,例如:Vue3、Angular、Svelte。
其本质原因在于模板语法时固定的,固定意味着“可分析”,“可分析”意味着在编译时可以标记模板语法中的静态部分(不变的部分)与动态部分(包含自变量、可变的部分)。
但采用JSX语法描述UI的前端框架很难从 AOT中受益,因为JSX是ES的语法糖,ES语句的灵活性使其很难进行静态分析。
拓展 那么Template语法是如何从中受益的呢?
模板语法由于在构建时已经被编译成可执行的JavaScript代码,运行时无需再进行解析和编译,从而减少了性能开销。
回归主题,根据前面的学习,我们知道了 JSX 方法执行后会返回一个新的 React 元素(ReactElement)。React 元素是一个轻量级的对象,描述了要渲染的 UI 组件的类型(type)、属性(props),和子元素(children)等信息。
这里可以给自己个问题,如果ReactElement作为reconciler核心模块操作的数据结构,会存在哪些问题:
从下图中可以看到,ReactELement这种数据结构很有限,在节点属性关联方面也只有 children,并没有保存兄弟节点以及父节点之间的关系:
当然在React 16版本之前,React 使用的是名为Stack Reconciler的旧调和算法。Stack Reconciler 的核心是递归遍历组件树,把数据保存在递归调用栈中。它使用的深层递归遍历方法。
但是使用递归遍历组件树时,会导致一些问题:
为了解决这些问题,React 引入了 Fiber Reconciler。Fiber Reconciler 使用了一种名为 "Fiber" 的新数据结构来表示组件树。
它的特点:
FiberNode 是虚拟DOM在React中的实现。
FiberNode Tree的数据结构如图所示:
FiberNode 上有很多属性,包括和自身相关的属性 ref,节点之间的关系 return、silbing还有工作单元上的属性,比如 pendingProps等等,后面会详细介绍。
Fiber最主要的两层含义:
Fiber的出现也为React带来了很多意义:
从优化层面来说,Fiber是一种新的调和算法(reconciliation algorithm)。
这两个概念会在后面的章节详细讲解。
Fiber是React的最小的工作单元。在React的世界中,一切都可以是组件。在普通的HTML页面上,开发者们可以将多个DOM元素整合在一起组成一个组件。
普通的DOM元素(HostComponent)可以是组件,普通的文本节点(HostText)也可以是组件。还有通过ReactDom.render方法创建的根元素(RootElement)也可以是组件,还有经常在React中使用的函数组件(FunctionComponent)。
在React源码中,每个FiberNode都有一个WorkTag属性,用于标识当前节点的类型。
// pagkages/react-reconciler/src/ReactWorkTags.ts
export type WorkTag =
| typeof FunctionComponent
| typeof ClassComponent
| typeof HostRoot
| typeof HostComponent
| typeof HostText
| typeof Fragment;
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const HostRoot = 3; // 通过ReactDom.render()产生的根元素
export const HostComponent = 5; // dom元素 比如
export const HostText = 6; // 文本类型 比如:123
export const Fragment = 7; //
ReactWorkTags.ts文件中定义了所有可能的节点类型,每个类型都应一个number类型的值。
这样做的好处是可以通过比较两个节点的 WorkTag 属性来判断它们是否是同一类型的节点,而不需要通过字符串比较等方式,这样可以提高比较的效率,也可以减少出错的可能性。
每一个组件都对应着一个FiberNode,许多个FiberNode互相嵌套、关联就组成了FiberNode Tree。正如下面表示的FiberNode Tree和DOM树的关系一样:
Fiber树 DOM树
div#root div#root
| |
div
| / \
div p a
/ ↖
/ ↖
p ---->
|
a
一个DOM节点一定对应着一个FiberNode,但每一个Fiber节点缺不一定有对应的DOM节点。
因为React支持不同类型的组件,因此每个FiberNode并不一定具有对应的DOM节点。
Fiber作为工作单元,它有很多属性:
// pagkages/react-reconciler/src/ReactFiber.js
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
) {
// Instance
this.tag = tag;
this.key = key;
this.type = null; // fiber对应的DOM元素的标签类型,div、p...
this.stateNode = null; // fiber的实例,类组件场景下,是组件的类,HostComponent场景,是dom元素
this.ref = null; // ref相关
// Fiber 除了有自身实例上的属性,还需要有表示和其它节点的关系
this.return = null; // 指向父级fiber
this.child = null; // 指向子fiber
this.sibling = null; // 同级兄弟fiber
this.index = 0;
// 作为工作单元与Fiber更新相关
this.pendingProps = pendingProps; // 刚开始工作阶段的 props
this.memoizedProps = null; // 工作结束时确定下来的 props
this.memoizedState = null; // 更新完成后的新 state
this.updateQueue = null; // Fiber产生的更新操作都会放在更新队列中
// Effects
this.flags = NoFlags; // 比如插入 更改 删除dom等)初始状态时表示没有任何标记(因为还没进行fiberNode对比)
this.subtreeFlags = NoFlags; // 子节点副作用标识
this.deletions = null; // 用于存放被删除的子节点
/*
* 可以看成是workInProgress(或current)树中的和它一样的节点,
* 可以通过这个字段是否为null判断当前这个fiber处在更新还是创建过程
* */
this.alternate = null; // 用于 current Fiber树和 workInProgress Fiber树的切换(如果当时fiberNode树是current树,则alternate指向的是workInProgress树)
}
这里Fiber节点的属性没有写完全,可以去react源码里看,地址在代码块首行。
虽然属性很多,但可以按三层含义将它们分类来看:
每个Fiber节点有个对应的React element,多个Fiber节点是如何连接形成树呢?靠如下三个属性:
// 指向父级Fiber节点
this.return = null;
// 指向子Fiber节点
this.child = null;
// 指向右边第一个兄弟Fiber节点
this.sibling = null;
举个例子,比如下面的组件结构:
function App() {
return (
i am
时光屋小豪
fighting
)
}
对应的FiberNode Tree结构:
作为静态的数据结构,需要保存组件的相关的信息:
// Fiber对应组件的类型 Function/Class/Host...
this.tag = tag;
// key属性
this.key = key;
// 大部分情况同type,某些情况不同,比如FunctionComponent使用React.memo包裹
this.elementType = null;
// 对于 FunctionComponent,指函数本身,对于ClassComponent,指class,对于HostComponent,指DOM节点tagName
this.type = null;
// Fiber对应的真实DOM节点
this.stateNode = null;
作为动态的工作单元,Fiber中如下参数保存了本次更新相关的信息,会在后续的更新流程章节中使用到具体属性时再详细介绍。
// 保存本次更新造成的状态改变相关信息
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// 保存本次更新会造成的DOM操作
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
如下两个字段保存调度优先级相关的信息,会在讲解Scheduler时介绍。
// 调度优先级相关
this.lanes = NoLanes;
this.childLanes = NoLanes;
在本节我们对Reconciler的架构有了大概的认知,了解了传统的库与现代框架的工作原理,也掌握了预编译和即时编译的区别,以及它们在现代框架中的应用。
在上一节中,我们实现了JSX的转换,知道了React Element这种数据,但是它也有一定的缺陷,为了解决这个缺陷,React 引入了Fiber架构,介绍了Fiber出现的意义,以及它的结构是什么样的,通过FiberNode组成的FiberNode Tree的结构。
下一节主要介绍Fiber作为Reconciler核心模块的工作单元,是如何创建及更新DOM的。
网页标题:「从0实现React18系列」Fiber架构的实现原理
路径分享:http://www.mswzjz.cn/qtweb/news36/493036.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能