React 15
1 React 组件使用流程的示意图

- 触发阶段:带有 React 标志的角色在 “Component Kitchen(组件厨房)” 选择 “Card(卡片)” 组件;
- 渲染阶段:该角色获取并渲染 “Card” 组件;
- 提交阶段:带有 React 标志的角色将 “Card” 组件呈现给用户(箭头标识的角色)。
它形象地解释了 React 从选择组件、渲染组件到向用户交付组件的过程,帮助开发者理解组件在 React 应用中的流转逻辑。
2 渲染必须是纯计算

一、纯计算的两个核心要求
输入相同,输出相同组件的渲染逻辑应是 “纯函数” 式的 —— 只要传入的
props、state等输入相同,返回的 JSX 结构和内容必须完全一致。比如一个展示用户信息的UserCard组件,传入用户{name: '张三', age: 25}时,每次都应渲染出包含 “张三,25 岁” 的卡片;若某次突然变成 “李四,30 岁”,就违反了这一原则。文中用 “点西红柿沙拉不应收到洋葱沙拉” 的类比,就是为了形象说明这种 “输入输出一致性” 的必要性。不产生副作用渲染过程中不能修改任何渲染前就存在的外部状态,比如全局变量、其他组件的
state、本地存储数据等。例如,若在渲染函数中直接修改window.globalCount的值,就会导致其他依赖该变量的组件行为异常,如同 “一个订单修改了其他人的订单”,破坏了程序的稳定性。
二、违反纯计算的后果
当渲染不是纯计算时,代码逻辑会变得难以追踪和维护:
- 错误难以排查:比如组件渲染结果随机变化,开发者无法通过输入快速定位问题根源。
- 行为不可预测:在 React 的并发渲染、重渲染机制下,不纯的渲染逻辑会引发各种边界情况的 bug,且复现路径不固定。
三、React 严格模式的作用
React 的 “严格模式(StrictMode)” 会主动调用组件函数两次,以此暴露不纯的渲染逻辑 —— 如果组件在两次调用中输出不同,或产生了副作用(如修改外部变量),开发者就能快速识别出问题,从而修复以保证渲染的纯计算特性。
3 React 把更改提交到 DOM 上

一、React 渲染后如何操作 DOM
- 初次渲染:React 会用
appendChild()这个 DOM 工具,把所有新创建的 DOM 节点(比如页面上的按钮、文字框)添加到页面上,就像把新做的菜端到餐桌上。 - 重渲染(页面更新):React 会只做必要的修改,让 DOM 和最新的渲染结果匹配。比如页面上只有时间变了,就只更新时间部分,其他不变,这样能提高效率。
二、React 什么时候会改 DOM
只有当渲染的内容和之前有差异时,React 才会修改 DOM 节点。
三、用例子理解(Clock 组件)
看那个Clock.js的代码和示例:
- 组件里有个显示时间的
<h1>和一个<input>输入框。 - 时间每秒都会变,所以 React 会更新
<h1>里的时间。 - 而
<input>在 JSX 里的位置和结构每次都一样,所以 React 不会动它,你在输入框里输入的内容也不会因为页面重渲染而消失。
4 渲染与提交摘要

一、React 屏幕更新的三个步骤
- 触发:这是屏幕更新的起点,常见的触发原因有三种
- 组件初始挂载:比如页面刚加载时,React 把组件渲染到页面上。
- 状态(State)变化:当你在组件中使用
useState等钩子修改状态时,会触发更新。例如点击按钮修改一个计数器的数值。 - 属性(Props)变化:父组件传递给子组件的属性发生改变时,子组件会被触发更新。比如父组件的一个变量变化了,传给子组件后子组件重新渲染。
- 渲染:在这个阶段,React 会调用组件函数,计算出最新的 JSX 结构。这一步是纯计算过程,就像根据订单在厨房制作菜品,只负责生成 “菜品”(JSX),不直接和用户(DOM)交互。而且如果这次渲染的结果和上次完全一样,React 就会跳过后续的提交步骤,避免不必要的 DOM 操作。
- 提交:这是将渲染结果同步到真实 DOM 的阶段。
- 初次渲染时,React 会用
appendChild等 DOM API 把所有新的 DOM 节点添加到页面上。 - 重渲染时,React 会对比前后两次的渲染结果,只对有差异的部分进行 DOM 修改,以此保证性能高效。
- 初次渲染时,React 会用
二、严格模式的作用
React 的严格模式(StrictMode)是一个用于开发环境的工具,它可以帮助开发者发现组件中的潜在问题。它会刻意重复调用组件的某些生命周期方法或函数组件,以此来检测那些不符合 React 最佳实践的代码,比如不纯的渲染逻辑、过时的 API 使用等。在生产环境中,严格模式不会有任何额外行为,所以不用担心它会影响应用性能。
三、渲染结果无差异时的 DOM 优化
如果当前渲染的结果和上一次完全相同,React 就不会对 DOM 进行任何修改。这是 React 的一种性能优化策略,避免了不必要的 DOM 操作,因为 DOM 操作相对来说是比较耗时的。例如一个组件的状态变化后,重新渲染的 JSX 和之前一模一样,那么页面上的真实 DOM 就不会有任何变化。
5 React 工作机制的可视化解释


第一张图:React 渲染流程
- 首先,React 执行组件函数(图中以持有 “Form” 的 React 标识人物体现)。
- 接着,计算组件的快照(通过带有 “Sent!” 和 “JSX” 放大镜的画面展示,代表对 JSX 结构的处理)。
- 最后,根据快照更新 DOM 树(React 标识人物操作 DOM 结构示意图)。这一流程体现了 React 从组件逻辑到界面渲染的过程。
第二张图:React 状态更新流程
- 当触发更新操作(如点击事件触发
setUpdate),React 收到通知。 - 随后,React 更新 state 的值(图中 “isSent” 从 false 变为 true 的状态变更)。
- 最后,React 向组件传入更新后的 state 快照,组件据此重新渲染(展示 Form 组件因 state 变化而更新界面)。这一流程解释了 React 中状态管理与界面更新的联动机制。
- 当触发更新操作(如点击事件触发
6 代码解释React 状态更新的 “批量处理” 和 “闭包特性”
import { useState } from 'react';export default function Counter() {const [number, setNumber] = useState(0);return (<><h1>{number}</h1><button onClick={() => {setNumber(number + 1);setNumber(number + 1);setNumber(number + 1);}}>+3</button></>)
}
在这个代码中,点击按钮后,数字只会增加 1,而不是预期的 3。原因在于React 状态更新的 “批量处理” 和 “闭包特性”:
状态更新的 “批量处理”React 会将多个连续的
setNumber调用合并为一次更新(批量处理),避免频繁渲染。这意味着多次调用不会立即生效,而是等待当前事件处理函数执行完毕后,统一计算最终状态。“闭包” 导致的状态值固定每次调用
setNumber(number + 1)时,number的值是事件处理函数创建时捕获的 “旧值”(即 0)。因为闭包会保留当时的变量状态,所以三次调用实际都是基于number = 0计算的,等效于:setNumber(0 + 1); // 结果还是 1 setNumber(0 + 1); // 依然是 1 setNumber(0 + 1); // 还是 1最终合并后的更新只会让
number从 0 变为 1。
如何让数字增加 3?如果需要基于上一次更新后的状态计算新值,应使用函数式更新(传入回调函数),例如:
<button onClick={() => {setNumber(n => n + 1); // 基于上一次结果 n 计算setNumber(n => n + 1);setNumber(n => n + 1);
}}>+3</button>
此时,每次更新都会使用前一次更新后的结果(1→2→3),最终数字会正确增加 3。
7 state 如同一张快照

这是一个关于 React 状态 “快照” 特性的代码示例,可分点解读如下:
现象说明
- 界面上数字显示为 15,说明已经多次点击了 “+5” 按钮。但点击按钮后,
setTimeout里的alert(number)弹出的是旧的 state 值(比如初始点击时弹出 0)。
- 界面上数字显示为 15,说明已经多次点击了 “+5” 按钮。但点击按钮后,
原理分析
- React 中,每次渲染的 state 是一个 “快照”,在当前函数作用域内固定不变。
setTimeout的回调函数捕获的是定义时所在渲染周期的 state 快照,即便后续 state 更新,这个回调里的number依然是旧值。
拓展理解
- 若想在
setTimeout中获取最新的 state,可通过useRef存储可变引用,或使用函数式更新的回调逻辑来获取。
- 若想在

8 React 的状态更新批处理机制

1. 批处理的作用
- React 会将多个
setState(或useState的更新函数)调用合并为一次重新渲染。比如图中多次调用setColor(setOrange、setPink、setBlue),最终只会触发一次 UI 更新。 - 这样做的好处是减少不必要的渲染次数,让应用运行更快,同时避免出现 “只更新了部分状态的半成品 UI”。
2. 批处理的时机
- 只有在事件处理函数(如点击、输入)执行完毕后,React 才会批量更新 UI。也就是说,在事件处理函数内部多次更新状态,不会立即反映到界面上。
3. 批处理的边界
- React 不会跨多个用户触发的事件(如多次点击)进行批处理。每次点击都是独立的处理过程,确保操作的独立性。例如第一次点击禁用表单后,第二次点击不会重复提交,保证了逻辑的正确性。
9 函数式更新


React 中状态更新函数(即 setXxx(prev => newVal) 这种函数式更新)的执行机制,本质是为了解决 “基于前一次状态计算新状态” 的问题,尤其是在多次连续更新时。我们可以从更新队列的形成、队列的处理时机、状态计算逻辑三个层面详细拆解:
1. 更新队列的形成:每次调用都被 “暂存”
当你在事件处理函数(如点击事件)中多次调用状态更新函数时,React 并不会立即执行这些函数并更新状态,而是会把它们依次加入一个 “更新队列”中暂时保存。
例如,连续三次调用函数式更新:
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
此时,React 的更新队列会记录这三个函数,形成一个队列:[n => n+1, n => n+1, n => n+1]。
2. 队列的处理时机:事件函数执行完后统一处理
React 会等待当前的事件处理函数完全执行完毕(即函数内所有代码都跑完),再开始处理更新队列。这是因为 React 会对状态更新进行 “批处理”,避免频繁更新导致的性能问题。
也就是说,在事件处理函数内部,无论你调用多少次 setXxx,状态都不会立即变化,直到函数执行结束,才会一次性处理所有更新。
3. 状态计算逻辑:链式依赖前一次结果
当处理更新队列时,React 会按顺序依次执行队列中的每个更新函数,并且每个函数的参数 prev 都是上一个更新函数执行后的结果(即最新的状态)。
以初始状态 number = 0 为例,处理上述队列的过程是:
- 执行第一个函数
n => n + 1:此时n是初始状态0,返回0 + 1 = 1; - 执行第二个函数
n => n + 1:此时n是上一次的结果1,返回1 + 1 = 2; - 执行第三个函数
n => n + 1:此时n是上一次的结果2,返回2 + 1 = 3。
最终,React 会将队列处理完毕后的结果(3)作为新的状态,触发组件重新渲染,界面上就会显示 3。
为什么需要这种机制?
如果不用函数式更新,而是直接用 setNumber(number + 1),由于闭包的特性,number 会捕获当前渲染周期的 “旧快照”(比如初始 0),三次调用都会基于 0 计算,最终结果只会是 1。
而函数式更新通过让每个更新依赖前一次的结果,完美解决了这个问题,确保多次连续更新能正确累计。这也是为什么在需要 “基于当前状态计算新状态” 时,推荐使用函数式更新的核心原因。
注意

- 当使用
setState(x)(或useState的状态设置函数直接传值,如setNumber(5))时,其执行逻辑等价于setState(n => x)(或setNumber(n => 5)),只是没有使用参数n(即不依赖之前的状态值)。 - 这说明直接传值的状态更新本质上也是一种函数式更新的简化形式,进一步体现了 React 状态更新的一致性机制。
10 React 状态管理核心机制的摘要总结
“设置 state 不会更改现有渲染中的变量,但会请求一次新的渲染”当你调用
setState(或useState的状态设置函数)时,当前渲染周期内的变量(如useState返回的状态变量)不会立即改变,它仍保持当前渲染的 “快照” 值。但 React 会标记需要进行一次新的渲染,在新的渲染中,组件函数会重新执行,此时useState会返回更新后的状态值,界面也会随之更新。例如,在一个点击事件中调用setNumber(5),当前函数内的number还是旧值,但 React 会在事件处理完成后触发新渲染,让number在新渲染中变为 5。“React 会在事件处理函数执行完成之后处理 state 更新。这被称为批处理”批处理是 React 的性能优化策略。在同一个事件处理函数(如点击、输入事件的回调)中,无论你调用多少次
setState,React 都会等到这个函数完全执行完毕后,再统一处理所有的状态更新,然后触发一次渲染。这样可以避免频繁渲染导致的性能问题,也能防止出现 “部分状态更新的半成品 UI”。比如在一个点击事件中连续三次调用setNumber(1)、setNumber(2)、setNumber(3),React 会在事件函数执行完后,直接将number更新为 3,然后触发一次渲染。“要在一个事件中多次更新某些 state,你可以使用 setNumber (n => n + 1) 更新函数”当需要在一次事件中基于前一次的状态结果进行多次更新时(如连续累加),直接传值的方式(如
setNumber(number + 1))会因为闭包捕获旧状态而出现问题。而使用函数式更新(setNumber(n => n + 1)),React 会将这些更新函数加入队列,在处理时依次执行,每个函数的参数n都是上一次更新后的结果,从而保证多次更新能正确累计。例如连续三次调用setNumber(n => n + 1),初始number为 0,最终会依次计算0+1=1、1+1=2、2+1=3,得到正确的结果 3。
