前端视界:图解React
本文从浅入深的全局解析 React,主要介绍其
基础概念
、版本演进
、生命周期
、Hooks
、生态链
、工程化
、性能优化
、核心API
、核心原理
和部分源码解析
引言
最近失业,想着把我了解的一些前端技术整理一下,写了《前端视界:我的技术与思考》这篇文章,方便自己查阅,也供大家一起学习。
但之前的文章,技术广度是有了,但深度不够,所以我想分析某个技术或方向,从浅入深的分析其基础概念、使用 API
到核心原理及部分源码解析。
今天,给大家带来一篇图解 React
的文章,从浅入深的全局解析 React,主要介绍其基础概念
、版本演进
、生命周期
、Hooks
、生态链
、工程化
、性能优化
、核心API
、核心原理
和部分源码解析
。
React的全局图解
React 的全局概念和生态是非常丰富的,那解析 React 就要从多个方面来解析。
-
基础概念
:介绍什么是React
、诞生背景
、重要性
、应用场景
和版本演进
。 -
版本演进
:介绍 React15
、16
、17
、18
和19
版本的主要特性和变化。 -
生命周期
:介绍括组件的挂载
、更新
和卸载
等阶段,以及常用的生命周期方法。 -
Hooks
:介绍useState
、useEffect
、useContext
等 hooks 方法,用于在函数组件中管理状态和副作用。 -
生态链
:包括UI 组件库
、路由库
、状态管理库
等,用于快速构建应用程序。 -
工程化
:包括构建工具
、模块打包
、测试编写
等,用于提高开发效率和代码质量。 -
性能优化
:包括代码分割
、懒加载
、Memoization
、性能监测
、分析工具
等,用于提高应用程序的性能。 -
核心API
:介绍React API
、ReactDOM API
、ReactDOMServer API
等,用于与 DOM 进行交互和测试。 -
核心原理
:包括单向数据流
、虚拟DOM
、JSX
、Fiber架构
、Diff算法
、事件系统等,用于实现 React 的核心功能。 -
部分源码解析
:包括 ReactDOM 的render
、update
、commit
等方法的实现,以及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.js
和 React Native
等。
React工程化和性能优化
选择 React 作为项目的开发框架时,那更看重的就是其在测试
、编译打包
和性能优化
方面的优势,我们还是有必要了解下其的工程化和性能优化方面的知识。
React核心API
上图中,React API
、ReactDOM API
和ReactDOMServer API
是 React 中最常用的 API,它们分别用于与 DOM
进行交互、与服务器端进行交互和在服务器端渲染 React 组件。
React核心原理
如上图所示,React 核心原理包括单向数据流
、虚拟DOM
、JSX
、Fiber架构
、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
链,获取每个需要commit
的fiber
对象。 -
如果
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
操作(如挂载
、更新
等)。