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