前端视界:图解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 操作(如挂载更新等)。

前端视界专栏

相关推荐
阿猿收手吧!1 小时前
【面试】面试常见的智力题
面试·职场和发展
Anlici2 小时前
还只会express?今儿使用Koa2 实现 Jwt鉴权
前端·koa
zhenryx2 小时前
前端-导出png,jpg,pptx,svg
开发语言·前端·javascript
硕风和炜2 小时前
【LeetCode: 8. 字符串转换整数 (atoi) + 模拟】
java·算法·leetcode·面试·模拟
QBorfy2 小时前
02篇 AI从零开始 - 部署本地大模型 DeepSeek-R1
前端·人工智能·deepseek
QBorfy3 小时前
01篇 AI从零开始 - 基础知识和环境准备
前端·人工智能
115432031q3 小时前
基于SpringBoot养老院平台系统功能实现十五
java·前端·后端
头顶秃成一缕光3 小时前
Springboot原理(面试高频)
spring boot·后端·面试
朦胧之3 小时前
React Native 新架构 (一)
前端·react native
患得患失9494 小时前
【前端】【面试】ref与reactive的区别
前端·面试·vue3