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方法

相关推荐
张拭心1 小时前
2024 总结,我的停滞与觉醒
android·前端
念九_ysl1 小时前
深入解析Vue3单文件组件:原理、场景与实战
前端·javascript·vue.js
Jenna的海糖1 小时前
vue3如何配置环境和打包
前端·javascript·vue.js
Mr.NickJJ1 小时前
React Native v0.78 更新
javascript·react native·react.js
星之卡比*1 小时前
前端知识点---库和包的概念
前端·harmonyos·鸿蒙
灵感__idea1 小时前
Vuejs技术内幕:数据响应式之3.x版
前端·vue.js·源码阅读
烛阴1 小时前
JavaScript 构造器进阶:掌握 “new” 的底层原理,写出更优雅的代码!
前端·javascript
Alan-Xia2 小时前
使用jest测试用例之入门篇
前端·javascript·学习·测试用例
浪遏2 小时前
面试官😏 :文本太长,超出部分用省略号 ,怎么搞?我:🤡
前端·面试
昕er2 小时前
CefSharp 文件下载和保存功能-监听前端事件
前端