react 整体的底层原理(基本构造)

221 阅读6分钟

React 的底层原理可以从「核心设计目标」「核心机制」「关键技术实现」三个层面拆解,核心是解决「高效更新 UI」和「简化复杂交互逻辑管理」的问题。以下是关键原理的梳理:

一、核心设计目标:为什么需要 React?

传统 DOM 操作存在两个核心问题:

1.	直接操作 DOM 性能差:DOM 是浏览器渲染引擎的接口,频繁操作(如增删改)会触发重排/重绘,消耗性能;

2.	复杂状态与 UI 同步难:当页面状态(如表单输入、接口数据)变化时,需要手动找到对应的 DOM 并更新,逻辑繁琐且易出错。

React 的核心目标:通过「声明式编程」和「高效更新机制」,让开发者只关注“状态对应什么 UI”,而非“如何更新 UI”。

二、核心机制:从「状态变化」到「UI 更新」的完整流程

React 的核心流程可以概括为:状态变化 → 计算 UI 差异 → 最小化更新 DOM,核心依赖 3 个关键技术:「虚拟 DOM」「协调(Reconciliation)算法」「Fiber 架构」。

  1. 虚拟 DOM(Virtual DOM):UI 的“内存映射”

虚拟 DOM 是 React 解决“直接操作 DOM 性能差”的核心设计。

•	本质:用 JavaScript 对象(通常是树结构)描述真实 DOM 的结构和属性(比如标签名、属性、子元素)。

例:真实 DOM

Hello
对应的虚拟 DOM 可能是: { type: 'div', // 标签类型(原生标签/组件) props: { className: 'box' }, // 属性 children: 'Hello' // 子元素 } • 作用:

◦	避免频繁操作真实 DOM:状态变化时,先在内存中生成新的虚拟 DOM,对比新旧虚拟 DOM 的差异(而非直接操作 DOM);

◦	跨平台能力:虚拟 DOM 与具体平台(浏览器 DOM、移动端原生组件)无关,React 可以通过不同的“渲染器”(如 ReactDOM、React Native)将虚拟 DOM 转换为对应平台的 UI(比如浏览器 DOM、iOS 原生 View)。

2. 协调(Reconciliation)算法:计算“最小更新差异”

当状态变化时,React 会生成新的虚拟 DOM 树,然后通过「协调算法」对比新旧虚拟 DOM 树,找到需要更新的最小部分(而非全量替换),这个过程也叫“Diff 算法”。

React 的 Diff 算法基于三个“启发式假设”(实际开发中大概率成立),以此简化计算、提升效率:

1.	同层节点类型不同 → 直接销毁旧节点,创建新节点:比如 <div> 变成 <p>,不会对比内部子元素,直接替换;

2.	同类型节点 → 对比属性,复用节点:比如 <div className="a"> 变成 <div className="b">,只更新 className 属性,不重新创建节点;

3.	同列表节点 → 通过 key 标识唯一性:列表渲染时(如 map 生成的节点),如果没有 key 或 key 不稳定(如用索引),React 可能误判节点的增删改,导致不必要的 DOM 操作(甚至状态错乱)。key 的作用是让 React 快速识别“哪些节点是复用的”“哪些是新增/删除的”。

例:列表 [A, B, C] 插入 D 变成 [A, D, B, C],有 key 时 React 能直接定位 D 是新增,B、C 是移动;无 key 时可能误判 B、C 被修改。

  1. Fiber 架构:解决“渲染阻塞”问题

React 16 之前的渲染是“同步且不可中断”的:如果虚拟 DOM 树很大(比如复杂列表),Diff 计算会占用主线程很长时间(几百毫秒),导致浏览器无法处理用户输入、动画等,出现卡顿。 Fiber 是 React 16 引入的新架构,核心是将渲染过程拆分为“可中断、可恢复、优先级可控”的小单元。

•	本质:Fiber 是一个数据结构(可以理解为“工作单元”),每个 Fiber 对应一个虚拟 DOM 节点,记录该节点的类型、属性、子节点、父节点,以及“当前处理状态”(是否完成、是否需要暂停等)。

•	工作流程:

1.	调度阶段(Scheduler):根据任务优先级(如用户输入 > 动画 > 普通更新)决定先处理哪个任务;

2.	协调阶段(Reconciliation):基于 Fiber 拆分任务(每个 Fiber 对应一个小任务),计算新旧虚拟 DOM 差异(Diff),标记需要更新的节点(如“更新属性”“插入节点”“删除节点”)。这个阶段可以被中断(比如有更高优先级任务插入时,暂停当前任务,保存进度,处理完高优先级任务后再恢复);

3.	提交阶段(Commit):根据协调阶段标记的“更新指令”,一次性操作真实 DOM(这个阶段不可中断,否则会导致 DOM 状态不一致)。

简单说:Fiber 让 React 能“忙里偷闲”——在复杂计算中暂停,先响应用户输入,再继续计算,避免卡顿。

三、状态管理:setState 与 Hooks 的底层逻辑

React 的核心是“状态驱动 UI”,状态变化是触发更新的源头,需要理解状态更新的底层规则。

  1. setState 的“异步更新”与“批处理”

类组件中 setState 并非立即更新状态,而是将更新请求加入队列,在合适时机(如当前事件循环结束前)批量处理,避免频繁渲染。

例: this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); // 最终 count 只 +1(两次更新被合并,基于初始值计算) 如果需要基于“上一次更新后的状态”计算,可以传函数: this.setState(prev => ({ count: prev.count + 1 })); this.setState(prev => ({ count: prev.count + 1 })); // 最终 count +2(基于前一次结果计算) 2. Hooks 的底层实现(以 useState 为例)

函数组件的 Hooks(如 useState useEffect)本质是“状态存储 + 生命周期模拟”,底层依赖两个核心设计:

•	Hooks 链表:函数组件每次渲染时,Hooks 的调用顺序是固定的(必须在组件顶层调用,不能在条件/循环中)。React 用一个“链表”存储每个 Hook 的状态(如 useState 的值、更新函数),通过“调用顺序”对应链表节点(第一个 useState 对应链表头,第二个对应下一个节点)。如果调用顺序变化(如条件判断跳过某个 Hook),会导致链表索引错乱,状态读取错误。

•	闭包缓存状态:useState 的更新函数(如 setCount)不会捕获当前渲染的状态,而组件内的变量(如 count)是“当前渲染快照”(闭包保存)。例:

function App() { const [count, setCount] = useState(0); const handleClick = () => { setTimeout(() => { console.log(count); // 点击时的 count(闭包缓存) }, 1000); }; return {count}; } 四、总结:React 底层原理的核心逻辑

1.	虚拟 DOM:用 JS 对象模拟 DOM,避免直接操作 DOM 的性能损耗;

2.	Diff 算法:通过类型、属性、key 快速计算差异,最小化 DOM 更新;

3.	Fiber 架构:将渲染拆分为可中断的小任务,解决同步渲染阻塞问题;

4.	状态驱动:setState/Hooks 通过队列和闭包管理状态更新,触发 UI 重新计算。

这些设计最终服务于一个目标:让开发者用“声明式”的方式写 UI,同时保证性能和可维护性。