当前位置: 首页 > news >正文

React 15

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

  • 触发阶段:带有 React 标志的角色在 “Component Kitchen(组件厨房)” 选择 “Card(卡片)” 组件;
  • 渲染阶段:该角色获取并渲染 “Card” 组件;
  • 提交阶段:带有 React 标志的角色将 “Card” 组件呈现给用户(箭头标识的角色)。

它形象地解释了 React 从选择组件、渲染组件到向用户交付组件的过程,帮助开发者理解组件在 React 应用中的流转逻辑。

2 渲染必须是纯计算

一、纯计算的两个核心要求

  1. 输入相同,输出相同组件的渲染逻辑应是 “纯函数” 式的 —— 只要传入的propsstate等输入相同,返回的 JSX 结构和内容必须完全一致。比如一个展示用户信息的UserCard组件,传入用户{name: '张三', age: 25}时,每次都应渲染出包含 “张三,25 岁” 的卡片;若某次突然变成 “李四,30 岁”,就违反了这一原则。文中用 “点西红柿沙拉不应收到洋葱沙拉” 的类比,就是为了形象说明这种 “输入输出一致性” 的必要性。

  2. 不产生副作用渲染过程中不能修改任何渲染前就存在的外部状态,比如全局变量、其他组件的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 屏幕更新的三个步骤

  1. 触发:这是屏幕更新的起点,常见的触发原因有三种
    • 组件初始挂载:比如页面刚加载时,React 把组件渲染到页面上。
    • 状态(State)变化:当你在组件中使用useState等钩子修改状态时,会触发更新。例如点击按钮修改一个计数器的数值。
    • 属性(Props)变化:父组件传递给子组件的属性发生改变时,子组件会被触发更新。比如父组件的一个变量变化了,传给子组件后子组件重新渲染。
  2. 渲染:在这个阶段,React 会调用组件函数,计算出最新的 JSX 结构。这一步是纯计算过程,就像根据订单在厨房制作菜品,只负责生成 “菜品”(JSX),不直接和用户(DOM)交互。而且如果这次渲染的结果和上次完全一样,React 就会跳过后续的提交步骤,避免不必要的 DOM 操作。
  3. 提交:这是将渲染结果同步到真实 DOM 的阶段。
    • 初次渲染时,React 会用appendChild等 DOM API 把所有新的 DOM 节点添加到页面上。
    • 重渲染时,React 会对比前后两次的渲染结果,只对有差异的部分进行 DOM 修改,以此保证性能高效。

二、严格模式的作用

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 状态更新的 “批量处理” 和 “闭包特性”

  1. 状态更新的 “批量处理”React 会将多个连续的 setNumber 调用合并为一次更新(批量处理),避免频繁渲染。这意味着多次调用不会立即生效,而是等待当前事件处理函数执行完毕后,统一计算最终状态。

  2. “闭包” 导致的状态值固定每次调用 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)。
  • 原理分析

    • React 中,每次渲染的 state 是一个 “快照”,在当前函数作用域内固定不变。
    • setTimeout的回调函数捕获的是定义时所在渲染周期的 state 快照,即便后续 state 更新,这个回调里的number依然是旧值。
  • 拓展理解

    • 若想在setTimeout中获取最新的 state,可通过useRef存储可变引用,或使用函数式更新的回调逻辑来获取。

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

1. 批处理的作用

  • React 会将多个setState(或useState的更新函数)调用合并为一次重新渲染。比如图中多次调用setColorsetOrangesetPinksetBlue),最终只会触发一次 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=11+1=22+1=3,得到正确的结果 3。

http://www.dtcms.com/a/577162.html

相关文章:

  • 浏览器开发者工具(尤其是 Vue Devtools 扩展)和 Vuex 的的订阅模式冲突
  • 网站建设核电程序员找工作的网站
  • 特殊三列布局需求
  • js(DOM)基础:11、DOM定义、事件、文档的加载、DOM查询1、DOM实现轮播图、DOM查询2、DOM实现全选
  • 想做个电影网站该怎么做阳春ycqq人才招聘信息
  • JavaScript的Web APIs 入门到实战(day4):DOM 进阶与日期对象(附巩固练习和案例讲解)
  • AtCoder Educational DP Contest 刷题记录Ⅰ
  • WPF CalcBinding简化判断逻辑
  • HarmonyOS NFC应用开发:构建分布式近场通信解决方案
  • Robinhood的再进化:从零佣金交易到链上金融超级应用
  • Rust开发实战之简单游戏开发(piston游戏引擎)
  • MK9019 Buck降压电路设计笔记(光伏发电应用优化版 - UVLO 7V设置)
  • 5118网站的功能郑州网站优化公司排名
  • MQTT协议之QoS0(<=1)、QoS1(>=1)、QoS2(=1)详解
  • Dify使用02-Dify集成Ollama
  • [免费]基于Python的Flask酒店客房管理系统【论文+源码+SQL脚本】
  • LeetCode 219.存在重复元素2
  • 【CS224N】《深度学习自然语言处理》完整版笔记
  • 广东企业网站建设推荐网站做收录
  • XC7Z020-1CLG484I Xilinx AMD FPGA Zynq-7000 SoC
  • 论文分享 | BARD-GS:基于高斯泼溅的模糊感知动态场景重建
  • FPGA—ZYNQ学习spi(六)
  • 多智能体医疗会诊系统
  • ETCD 压力测试脚本
  • kali的下载和安装【ISO安装】
  • 从标签到数据流:BarTender让“可追溯”更简单
  • 零基础学AI大模型之Embedding与LLM大模型对比全解析
  • 7.游戏逆向-pxxx-TUObjectArray分析
  • web214-web220
  • 通州北苑网站建设程序开发的基本步骤是什么?