前端视界:图解React

前端视界:图解React

本文从浅入深的全局解析 React,主要介绍其基础概念版本演进生命周期Hooks生态链工程化性能优化核心API核心原理部分源码解析

引言

最近失业,想着把我了解的一些前端技术整理一下,写了《前端视界:我的技术与思考》这篇文章,方便自己查阅,也供大家一起学习。

但之前的文章,技术广度是有了,但深度不够,所以我想分析某个技术或方向,从浅入深的分析其基础概念、使用 API 到核心原理及部分源码解析。

今天,给大家带来一篇图解 React 的文章,从浅入深的全局解析 React,主要介绍其基础概念版本演进生命周期Hooks生态链工程化性能优化核心API核心原理部分源码解析

React的全局图解

React 的全局概念和生态是非常丰富的,那解析 React 就要从多个方面来解析。

  • 基础概念:介绍什么是 React诞生背景重要性应用场景版本演进

  • 版本演进:介绍 React 1516171819 版本的主要特性和变化。

  • 生命周期:介绍括组件的挂载更新卸载等阶段,以及常用的生命周期方法。

  • Hooks:介绍 useStateuseEffectuseContext 等 hooks 方法,用于在函数组件中管理状态和副作用。

  • 生态链:包括 UI 组件库路由库状态管理库等,用于快速构建应用程序。

  • 工程化:包括构建工具模块打包测试编写等,用于提高开发效率和代码质量。

  • 性能优化:包括代码分割懒加载Memoization性能监测分析工具等,用于提高应用程序的性能。

  • 核心API:介绍 React APIReactDOM APIReactDOMServer API等,用于与 DOM 进行交互和测试。

  • 核心原理:包括 单向数据流虚拟DOMJSXFiber架构Diff算法、事件系统等,用于实现 React 的核心功能。

  • 部分源码解析:包括 ReactDOM 的 renderupdatecommit 等方法的实现,以及 Fiber 架构的核心算法。

React基础概念

React 是一个由 Meta(Facebook) 开发并开源的、用于构建用户界面的 JS 库。它的核心思想是通过组件化的方式来构建可重用的 UI 元素,常用于构建大型、复杂的前端应用程序。具有虚拟DOM、组件化、声明式编程和丰富的生态系统:库、工具和活跃的社区等。

因为其高性能组件化开发效率高跨平台等特点,常用场景包括:大型复杂的web应用、SPA单页应用、移动应用开发(RN)、SSR服务端渲染(NextJs)、桌面应用开发(Electron)等。

React版本演进

React 的版本迭代非常频繁,从 0.1 到 19 版本(目前主流的版本覆盖在 15 到 18),React 不断地优化和改进,使其更加稳定、高效和易用。

React生命周期

如上图所示,React 有三个主要的生命周期阶段:挂载更新卸载,针对这三个阶段可以进行一些性能优化,在目前主流的开发流程中,直接使用类组件和生命周期函数已经不多,所以我们可以使用函数组件和 hooks 来代替。

但为了更好的理解生命周期执行过程,我们可以看下面一张图:

React Hooks

如上所示,Hooks 是 React 16.8 版本引入的新特性,它允许在函数组件中使用状态和副作用。

React生态链

React 生态链非常丰富,包括UI组件库路由库状态管理库Next.jsReact Native 等。

React工程化和性能优化

选择 React 作为项目的开发框架时,那更看重的就是其在测试编译打包性能优化方面的优势,我们还是有必要了解下其的工程化和性能优化方面的知识。

React核心API

上图中,React APIReactDOM APIReactDOMServer API是 React 中最常用的 API,它们分别用于与 DOM 进行交互、与服务器端进行交互和在服务器端渲染 React 组件。

React核心原理

如上图所示,React 核心原理包括单向数据流虚拟DOMJSXFiber架构Diff算法事件系统等。

React部分源码解析

如上图所示,React 的源码解析涵盖了从初始化渲染到更新渲染的整个过程,包括 Fiber 数据结构的创建、组件的渲染和更新、事件处理等。通过深入理解这些源码,可以更好地掌握 React 的工作原理和核心机制,从而更有效地进行前端开发和优化。

但因为xmind里不太好插入代码段进行解析,下面我们逐步拆解上图的部分源码解析:

legacyRenderSubtreeIntoContainer 函数

js 复制代码
function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>,
  children: ReactNodeList,
  container: Container,
  forceHydrate: boolean,
  callback: ?Function
) {
  let root: RootType = (container._reactRootContainer: any);
  let fiberRoot;

  if (!root) {
    // 如果容器未初始化过,调用 legacyCreateRootFromDOMContainer 创建根
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
    fiberRoot = root._internalRoot;

    // 如果有回调函数,调整其 this 指向
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function () {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }

    // 确保初始渲染尽快完成,不被打断
    unbatchedUpdates(() => {
      updateContainer(children, fiberRoot, parentComponent, callback);
    });
  } else {
    fiberRoot = root._internalRoot;
    if (typeof callback === 'function') {
      const originalCallback = callback;
      callback = function () {
        const instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    }
    // 更新操作
    updateContainer(children, fiberRoot, parentComponent, callback);
  }

  // 返回公共实例
  return getPublicRootInstance(fiberRoot);
}

解析

  • 检查容器是否已初始化:通过 container._reactRootContainer 判断容器是否已初始化。

  • 如果是初始渲染,调用 legacyCreateRootFromDOMContainer 创建根。

  • 调整回调函数的 this 指向,使其指向真实的 DOM 对象。

  • 通过 unbatchedUpdates 确保初始渲染尽快完成,不被打断。

legacyCreateRootFromDOMContainer 函数

js 复制代码
function legacyCreateRootFromDOMContainer(container: Container, forceHydrate: boolean): RootType {
  const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);

  // 如果不是服务器端渲染,清空容器中的节点
  if (!shouldHydrate) {
    let rootSibling;
    while ((rootSibling = container.lastChild)) {
      container.removeChild(rootSibling);
    }
  }

  // 创建 LegacyRoot
  return createLegacyRoot(container, shouldHydrate ? {hydrate: true} : undefined);
}

解析

  • 判断是否为服务器端渲染,通过 forceHydrate 参数和 shouldHydrateDueToLegacyHeuristic 函数来判断。

  • 如果不是服务器端渲染,清空容器中的节点

  • 调用 createLegacyRoot 创建 LegacyRoot

createLegacyRoot 函数

js 复制代码
export function createLegacyRoot(container: Container, options?: RootOptions): RootType {
  return new ReactDOMBlockingRoot(container, LegacyRoot, options);
}

解析

  • 通过 new ReactDOMBlockingRoot(container, LegacyRoot, options) 创建 ReactDOMBlockingRoot 实例。

  • 在实例化过程中,调用 createRootImpl 创建根 Fiber 数据结构

createRootImpl 函数

js 复制代码
function createRootImpl(container: Container, rootType: RootType, options: RootOptions): FiberRoot {
  // 调用 createFiberRoot 创建容器对象 FiberRootNode
  const fiberRoot = createFiberRoot(container, rootType, options);

  // markContainerAsRoot 将容器标记为根
  markContainerAsRoot(rootType, container);

  // 返回创建好的根 Fiber 数据结构
  return fiberRoot;
}

解析

  • 调用 createFiberRoot 创建容器对象 FiberRootNode

  • markContainerAsRoot 将容器标记为根。

  • 返回创建好的根 Fiber 数据结构。

createContainer 函数

js 复制代码
function createFiberRoot(
  container: Container,
  rootType: RootType,
  options: RootOptions
): FiberRoot {
  // 初始化 FiberRoot 对象,并设置其相关属性,如 container(容器)、tag(LegacyRoot 标记)、hydrate(是否为服务器端渲染)等。
  const fiberRoot: FiberRoot = {
    container,
    tag: LegacyRoot,
    current: null,
    finishedWork: null,
    context: null,
    pendingContext: null,
    hydrate: options?.hydrate || false,
    //...
  };

  return fiberRoot;
}

解析

  • 初始化 FiberRoot 对象,并设置其相关属性,如 container(容器)、tag(LegacyRoot 标记)hydrate(是否为服务器端渲染)等。

reconcileSingleTextNode 函数

js 复制代码
function reconcileSingleTextNode(returnFiber: Fiber, existingChild: Node, textContent: string) {
  // 如果存在现有子节点且是文本节点,更新文本内容
  if (existingChild.nodeType === TEXT_NODE) {
    existingChild.nodeValue = textContent;
    return existingChild;
  }
  // 否则创建新的文本节点
  else {
    const textNode = document.createTextNode(textContent);
    return textNode;
  }
}

解析

  • 如果当前子节点是文本节点,则更新其文本内容。

  • 如果不是文本节点,则创建新的文本节点并返回。

reconcileSingleElement 函数

js 复制代码
function reconcileSingleElement(
  returnFiber: Fiber,
  existingChild: Node,
  element: ReactElement
): Fiber {
  // 根据 React Element 创建 Fiber 对象
  const fiber = createFiberFromElement(element);
  // 设置 fiber 的父 Fiber 对象和 ref 属性。
  fiber.return = returnFiber;
  fiber.ref = element.ref;
  return fiber;
}

解析

  • 根据 React Element 创建 Fiber 对象。

  • 设置 fiber 的父 Fiber 对象和 ref 属性。

reconcileChildFibers 函数

js 复制代码
function reconcileChildFibers(
  returnFiber: Fiber,
  currentFirstChild: Fiber | null,
  newChildren: any
): Fiber {
  if (Array.isArray(newChildren)) {
    // 处理数组类型的子节点
    //...
  } else if (typeof newChildren === 'object' && newChildren !== null) {
    // 处理单个对象类型的子节点
    //...
  } else if (typeof newChildren === 'string' || typeof newChildren === 'number') {
    // 处理文本或数值类型的子节点
    //...
  }
  return null;
}

解析

  • 根据子节点的类型(数组、对象、文本或数值)进行相应的处理。

commitBeforeMutationEffects 函数

js 复制代码
function commitBeforeMutationEffects(fiber: Fiber) {
  if (fiber.effectTag & Snapshot) {
    // 调用 getSnapshotBeforeUpdate 生命周期函数
    commitBeforeMutationEffectOnFiber(fiber);
  }
  if (fiber.child) {
    // 递归处理子节点
    commitBeforeMutationEffects(fiber.child);
  }
  if (fiber.sibling) {
    // 递归处理同级节点
    commitBeforeMutationEffects(fiber.sibling);
  }
}

解析

  • 循环 effect 链,获取每个需要 commitfiber 对象。

  • 如果 fiber 对象有 Snapshot 标记,则调用 commitBeforeMutationEffectOnFiber 函数。

commitBeforeMutationLifeCycles 函数

js 复制代码
function commitBeforeMutationLifeCycles(fiber: Fiber) {
  // 判断 fiber 类型是否为类组件
  if (fiber.tag === ClassComponent) {
    const instance = fiber.stateNode;
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      // 如果是类组件且有 getSnapshotBeforeUpdate 生命周期函数,则调用该函数。
      instance.getSnapshotBeforeUpdate(fiber.memoizedProps, fiber.memoizedState);
    }
  }
}

解析

  • 判断 fiber 类型是否为类组件
  • 如果是类组件且有 getSnapshotBeforeUpdate 生命周期函数,则调用该函数。

commitMutationEffects 函数

js 复制代码
function commitMutationEffects(fiber: Fiber) {
  if (fiber.effectTag & Placement) {
    // 执行 DOM 挂载操作
    commitPlacement(fiber);
  }
  if (fiber.effectTag & Update) {
    // 执行 DOM 更新操作
    commitUpdate(fiber);
  }
  if (fiber.child) {
    // 递归处理子节点
    commitMutationEffects(fiber.child);
  }
  if (fiber.sibling) {
    // 递归处理同级节点
    commitMutationEffects(fiber.sibling);
  }
}

解析

  • 循环 effect 链,获取每个需要 commit 的 fiber 对象。
  • 根据 fiber 对象的 effectTag 执行相应的 DOM 操作(如挂载更新等)。

前端视界专栏

相关推荐
jump_jump14 分钟前
妙用 localeCompare 获取汉字拼音首字母
前端·javascript·浏览器
U.2 SSD22 分钟前
Echarts单轴坐标系散点图
前端·javascript·echarts
德育处主任Pro32 分钟前
前端玩转大模型,DeepSeek-R1 蒸馏 Llama 模型的 Bedrock 部署
前端·llama
程序员飞哥32 分钟前
如何设计多级缓存架构并解决一致性问题?
java·后端·面试
Jedi Hongbin1 小时前
Three.js NodeMaterial 节点材质系统文档
前端·javascript·three.js·nodematerial
前端小马1 小时前
前后端Long类型ID精度丢失问题
java·前端·javascript·后端
用户1456775610371 小时前
干净的图片批量处理,处理速度飞快
前端
用户1456775610372 小时前
亲测好用!简单实用的图片尺寸调整工具
前端
索西引擎2 小时前
npm、yarn、pnpm
前端·npm·node.js
天生我材必有用_吴用2 小时前
Vue3 + VitePress 搭建组件库文档平台(结合 Element Plus 与 Arco Design Vue)—— 超详细图文教程
前端