react中的fiber和初次渲染

源码中定义了不同类型节点的枚举值

组件类型

  • 文本节点
  • HTML标签节点
  • 函数组件
  • 类组件
  • 等等

src/react/packages/react-reconciler/src/ReactWorkTags.js

javascript 复制代码
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;

什么是fiber

A Fiber is work on a Component that needs to be done or was done. There can be more than one per component.

fiber是指组件上将要完成或者已经完成的任务,每个组件可以一个或者多个。

fiber结构

为什么需要fiber

  1. 为什么需要fiber

    对于大型项目,组件树会很大,这个时候递归遍历的成本就会很高,会造成主线程被持续占用,结果就是主线程上的布局、动画等周期性任务就无法立即得到处理,造成视觉上的卡顿,影响用户体验。

  2. 任务分解的意义

    解决上面的问题

  3. 增量渲染(把渲染任务拆分成块,匀到多帧)

  4. 更新时能够暂停,终止,复用渲染任务

  5. 给不同类型的更新赋予优先级

  6. 并发方面新的基础能力

  7. 更流畅

创建fiber结构

fiber就是一个js对象来抽象vnode

javascript 复制代码
function createFiber(vnode, returnFiber) {
  const fiber = {
    type: vnode.type,
    key: vnode.key,
    stateNode: null, // 原生标签时候指dom节点,类组件时候指的是实例
    props: vnode.props,
    child: null, // 第一个子fiber
    sibling: null, // 下一个兄弟fiber
    return: returnFiber, // 父节点
    // 标记节点是什么类型的
    flags: Placement,
    deletions: null, // 要删除子节点 null或者[]
    index: null, //当前层级下的下标,从0开始
    // 记录上一次的状态 函数组件和类组件不一样
    memorizedState: null,
    // old fiber
    alternate: null,
  };
  const { type } = vnode;

  if (isStr(type)) {
    // 原生标签
    fiber.tag = HostComponent;
  } else if (isFn(type)) {
    // 函数组件或者是类组件
    fiber.tag = type.prototype.isComponent ? ClassComponent : FunctionComponent;
  } else if (isUndefined(type)) {
    fiber.tag = HostText;
    fiber.props = { children: vnode };
  } else {
    fiber.tag = Fragment;
  }

  return fiber;
}

深度优先遍历每个fiber

对不同的类型节点tag,都有对应的处理方法

javascript 复制代码
function performUnitOfWork() {
  const { tag } = wip;

  switch (tag) {
    // 原生标签 比如div span button p a
    case HostComponent:
      updateHostComponent(wip);
      break;
    case FunctionComponent:
      updateFunctionComponent(wip);
      break;
    case ClassComponent:
      updateClassComponent(wip);
      break;
    case Fragment:
      updateFragmentComponent(wip);
      break;
    case HostText:
      updateHostTextComponent(wip);
      break;
    default:
      break;
  }

  if (wip.child) {
    wip = wip.child;
    return;
  }
  let next = wip;
  while (next) {
    if (next.sibling) {
      wip = next.sibling;
      return;
    }
    next = next.return;
  }
  wip = null;
}

初次渲染

在react项目中我们都是通过以下方法来初始化组件

javascript 复制代码
ReactDOM.createRoot(document.getElementById("root")).render(jsx);

那我们就来实现一下该createRoot和render方法

源码中的render是挂载到了原型对象上

javascript 复制代码
// react-dom
import createFiber from "./ReactFiber";
import { scheduleUpdateOnFiber } from "./ReactFiberWorkLoop";

// 构造函数
function ReactDOMRoot(internalRoot) {
  this._internalRoot = internalRoot;
}

ReactDOMRoot.prototype.render = function (children) {
  // 最原始的vnode节点(jsx) 我们需要的是fiber结构的vnode
  const root = this._internalRoot;
  // 原生dom节点
  console.log(root, "root");
  updateContainer(children, root);
};

// 初次渲染 组件到g根dom节点上
function updateContainer(element, container) {
  const { containerInfo } = container;
  const fiber = createFiber(element, {
    type: containerInfo.nodeName.toLocaleLowerCase(),
    stateNode: containerInfo,
  });
  // 组件初次渲染
  scheduleUpdateOnFiber(fiber);
}
function createRoot(container) {
  const root = { containerInfo: container };
  return new ReactDOMRoot(root);
}

// 一整个文件是ReactDOM, createRoot是ReactDOM上的一个方法
export default { createRoot };

后面湖会继续补充react是如何完成后续的渲染流程的 scheduleUpdateOnFiber方法

相关推荐
lee57610 小时前
鄙人的 Vue 3.0 商业级开源甘特图已经发布到 npm
前端·vue.js·npm·开源·甘特图
前端老曹10 小时前
vue3 三级路由无法缓存的终终终终终终极解决方案
前端·javascript·vue.js
零Suger10 小时前
React Router v7数据模式使用指南
javascript·笔记·react.js
顾安r10 小时前
12.17 脚本网页 创意导航
java·linux·前端·游戏·html
小明记账簿10 小时前
CSS mix-blend-mode 实现元素融合效果
前端·css
_Kayo_11 小时前
React 动态显示icon
前端·react.js·react
free-elcmacom11 小时前
Python实战项目<3>赛制分数分析
开发语言·前端·python·数据分析
Mr.Jessy16 小时前
JavaScript高级:构造函数与原型
开发语言·前端·javascript·学习·ecmascript
白兰地空瓶18 小时前
🚀你以为你在写 React?其实你在“搭一套前端操作系统”
前端·react.js
爱上妖精的尾巴18 小时前
6-4 WPS JS宏 不重复随机取值应用
开发语言·前端·javascript