fiber的构建和任务执行

194 阅读4分钟

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

fiberRootNode.png

如果组件是原生标签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() {
    
}

准备工作就绪!!!