fiber
本质还是vdom,主要是任务分解,给予不同类型的更新赋予优先级。
组件类型
文本节点,html标签,函数组件,类组件。。。 不可能每次渲染都判断组件类型的,所以给每个js对象加个值用以判断组件类型
react/packages/react-reconciler/src/ReactWorkTags.js
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;//原生标签
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const ScopeComponent = 21;
export const OffscreenComponent = 22;
export const LegacyHiddenComponent = 23;
export const CacheComponent = 24;
这是react源码里的fiber
如果组件是原生标签fiberNode下又个stateNode属性代表该原生标签
tag属性代表该组件的类型
child属性指向其第一个子节点,下一个子节点是放在child的sibing属性中,以次类推
注:函数组件和类组件本身是没节点的,只有子节点,因为都是靠retrun出去的
函数组件的stateNode指向类组件的实例对象
retrun属性指向其父节点
alternate:更新时的老节点
deletions:要删除的子节点
flags:节点要干什么事,比如插入,移动等
index:当前节点是第几个子节点(下标)
key:用于对比的唯一标识
memoizedProps:更新上去的属性值
pendingProps:还未更新的属性值
memoizedState:state的状态
构建fiber
🌟🌟🌟 1.创建一个 ReactFiber.js
filber对象:
类型:type,tag
唯一标识:key
属性:props
下标:index
操作:flage
本身的节点:stateNode
父节点:retrun
第一个子节点:child
兄弟节点:sibling
「代码」
import { FunctionComponent, HostComponent } from "./ReactWorkTags";
import { isFn, isStr, Placement } from "./utils";
/**
* @ vnode:接收的js对象,通过jsx可以拿到
* @ returnFiber:父节点
*/
export function createFiber(vnode, returnFiber) {
const fiber = {
type: vnode.type, //组件类型
key: vnode.key,
props: vnode.props, //属性
//不同类型的组件stateNode也不同
//原生标签:deom节点
//class: 实例
//function:null
stateNode: null,
child: null, //第一个子fiber
sibling: null, //下一个兄弟节点
return: returnFiber, //父节点
flags: Placement, //操作
index: null, //节点在当前层级的位置
};
const { type } = vnode;
if(isStr(type)){ //如果是字符串证明拿到的是原生标签
fiber.tag = HostComponent
}else if(isFn(type)){ //如果是函数证明拿到的是函数式组件或者类组件
fiber.tag = FunctionComponent
}
return fiber;
}
2.创建一个ReactWorkTags.js (复制上面的)
这个就是用来判断type生成对应的tag值
3.创建一个工具函数 utils 其中最重要的三个:代表flage属性的操作
Placement:插入/新增, update:节点更新, deletion:删除
flags定义为二进制,而不是字符串或者单个数字, 一方面原因是因为二进制单个数字具有唯一性、某个范围内的组合同样具有唯一性, 另一方原因在于简洁方便、且速度快。
比如要更新和新增那么就是2+4
// ! flags
export const NoFlags = /* */ 0b00000000000000000000;
//新增,插入
export const Placement = /* */ 0b0000000000000000000010; // 2
//节点更新
export const Update = /* */ 0b0000000000000000000100; // 4
//删除
export const Deletion = /* */ 0b0000000000000000001000; // 8
export function isStr(s) {
return typeof s === "string";
}
export function isStringOrNumber(s) {
return typeof s === "string" || typeof s === "number";
}
export function isFn(fn) {
return typeof fn === "function";
}
export function isArray(arr) {
return Array.isArray(arr);
}
🌟🌟🌟 4.创建一个ReactWorkLoop.js 这个就是对节点进行判断,更新那个节点
import {
updatedClassComponent,
updatedFragmentComponent,
updatedFunctionComponent,
updatedHostComponent,
updatedHostTextComponent,
} from "./ReactReconciler";
import {
ClassComponent,
Fragment,
FunctionComponent,
HostComponent,
HostText,
} from "./ReactWorkTags";
let wip = null; //当前正在工作中的fiber
//节点更新
function performUnitOfWork() {
let { tag } = wip;
//1.更新当前组件
switch (tag) {
case HostComponent:
updatedHostComponent(wip);
break;
case FunctionComponent:
updatedFunctionComponent(wip);
break;
case ClassComponent:
updatedClassComponent(wip);
break;
case Fragment:
updatedFragmentComponent(wip);
break;
case HostText:
updatedHostTextComponent(wip);
break;
default:
break;
}
//2.下一个更新谁? 深度优先遍历
//子组件 > 兄弟组件 > 父组件的兄弟组件
if (wip.child) {
wip = wip.child;
return;
}
let next = wip;
while (next) {
if (next.sibling) {
wip = next.sibling;
return;
}
//本身没有子节点,兄弟节点,就往上找父节点的兄弟节点
next = next.return;
}
wip = null;
}
5.创建一个 ReactReconciler.js 这个就是更新节点的具体操作
export function updatedHostComponent() {
}
export function updatedFunctionComponent() {
}
export function updatedClassComponent() {
}
//不想渲染的
export function updatedFragmentComponent() {
}
export function updatedHostTextComponent() {
}
准备工作就绪!!!