- 最近快期末考试,本来不打算写博客的,但是一旦停下不知又是何年,或许是我工作之后,也或许是永远把。毕竟这只是用来记录我大学的殷实生活,大四我不再着重记录,而是投身于找工作。
- 时光匆匆,重大一开始写博客起,也已经写了八九十篇了,每次回顾以前的博客时,就是感到时间快,真他娘快,转眼三年,看着每一篇博客,想着每一周的经历,感觉不那么真实,好像仿佛回到了那几天,这一切终究是要结束的,天下无不散之宴席
- 这篇文章是上个月我记的笔记与总结,我之前也阅读过vue源码的大概流程,还有部分api的细节,我明显的对比出react源码的相对复杂,感觉facebook那群团队就是操作系统出身一般。有很多跨领域的技术,比如双缓冲,我之前在写c++游戏时用esayX有过先行了解。总之react确实极其有意思,能学到很多思路。
- 下面是一些相关名词的介绍,一开始我对这些确实有些困惑的,好在如今网络发达,资源也很多,甚至b栈都有相关的源码讲解,可惜没找到18版本的。不过网上的资源也不能全信,有些人也是一知半解就发出来的,我被错误的引导两天,当时那是越看越懵。
Fiber 架构背景知识
在 React 中,为了实现高效的更新机制,引入了 Fiber 架构。Fiber 是一种数据结构,用于描述 React 组件的工作单元。每个 Fiber 节点对应一个 React 组件实例或者 DOM 元素。
- 我们提到的虚拟DOM在React中有个正式的称呼------Fiber
React 在更新过程中,会构建和处理 Fiber 树。这棵树用于跟踪组件的状态、属性以及它们之间的关系等诸多信息。
current和alternate属性的含义
- 当前屏幕上显示内容对应的Fiber树称为current Fiber树,正在内存中构建的Fiber树称为workInProgress Fiber树
current
是指向当前正在屏幕上显示的 Fiber 树的引用
。它代表了当前用户看到的 UI 对应的组件树结构
。alternate
是与之对应的另一个 Fiber 树的引用。在 React 的更新过程中,React 会构建一个新的 Fiber 树(称为workInProgress
树),这个新树的每个节点的alternate
属性会指向旧的(current)树中对应的节点。- 例如,当一个组件的状态发生改变,React 会开始构建一个新的 Fiber 树来反映这个更新。在构建过程中,新树的节点通过
alternate
属性与旧树的节点关联起来。这样做的目的是方便在更新完成后,将新树替换旧树(current树),实现高效的 UI 更新。
代码示例帮助理解
- 假设我们有一个简单的 React 组件:
js
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
- 在 React 内部,当
setCount
被调用触发组件更新时,React 会创建一个新的 Fiber 树(workInProgress树)
。新树中代表Counter组件的 Fiber 节点的alternate
属性会指向旧的(current)树中对应的Counter组件的 Fiber 节点。 - 这种双缓冲(current和alternate)的机制允许 React 在后台构建新的 UI,同时保持旧的 UI 在屏幕上显示,直到新的 UI 完全构建好并且可以无缝切换,从而避免在更新过程中出现 UI 的闪烁或者不一致等问题。
hostRootFiber
- hostRootFiber是 React Fiber 树的根节点。在 React 应用中,整个组件树结构是基于 Fiber 架构构建的,而hostRootFiber就是这个树形结构的起点。
- 它主要用于管理整个应用的渲染过程,包括协调子组件的更新、挂载和卸载等操作。从概念上讲,它类似于传统 DOM 树中的document对象,是整个渲染流程的控制中心。
- 在 Web 环境中,hostRootFiber与浏览器的 DOM 相关联。它负责将 React 组件树渲染成实际的 DOM 元素。当 React 应用首次加载时,hostRootFiber会创建并挂载初始的 DOM 节点,后续的更新操作也会通过hostRootFiber来更新对应的 DOM 部分。
- 例如,在一个简单的 React Web 应用中,hostRootFiber会对应 HTML 文件中的
(假设这是 React 应用挂载的根 DOM 节点)。React 会将整个组件树挂载到这个root节点上,而hostRootFiber就是这个挂载过程的关键节点,它管理着从 React 组件到实际 DOM 元素的转换。
两种重要的数据结构
Fiber
js
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// 作为静态数据结构的属性
this.tag = tag;
this.key = key;
this.elementType = null;// 大部分情况同type,某些情况不同,比如FunctionComponent使用React.memo包裹
this.type = null;// 对于 FunctionComponent,指函数本身,对于ClassComponent,指class,对于HostComponent,指DOM节点tagName
this.stateNode = null;// Fiber对应的真实DOM节点
// 用于连接其他Fiber节点形成Fiber树
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
// 作为动态的工作单元的属性。 保存本次更新造成的状态改变相关信息
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;
// 调度优先级相关
this.lanes = NoLanes;
this.childLanes = NoLanes;
// 指向该fiber在另一次更新时对应的fiber
this.alternate = null;
}
这里需要提一下,为什么父级指针叫做return而不是parent或者father呢?因为作为一个工作单元,return指节点执行完completeWork后会返回的下一个节点。子Fiber节点及其兄弟节点完成工作后会返回其父级节点,所以用return指代父级节点
ReactElement
js
export type ReactElement = {|
// 用于辨别ReactElement对象
$$typeof: any,
// 内部属性
type: any, // 表明其种类
key: any,
ref: any,
props: any,
// ReactFiber 记录创建本对象的Fiber节点, 还未与Fiber树关联之前, 该属性为null
_owner: any,
// __DEV__ dev环境下的一些额外信息, 如文件路径, 文件名, 行列信息等
_store: {validated: boolean, ...},
_self: React$Element<any>,
_shadowChildren: any,
_source: Source,
|};