React:状态管理 官网笔记

文章目录

  • [一、使用 State 响应输入](#一、使用 State 响应输入)
  • 二、选择状态结构
    • [1. 合并关联的 state](#1. 合并关联的 state)
    • [2. 避免矛盾的 state](#2. 避免矛盾的 state)
    • [3. 避免冗余的 state](#3. 避免冗余的 state)
    • [4. 避免重复的 state](#4. 避免重复的 state)
    • [5:避免深度嵌套的 State](#5:避免深度嵌套的 State)
  • 三、在组件间共享状态
    • [1. 什么是状态提升?](#1. 什么是状态提升?)
    • [2. 实现状态提升的三个步骤](#2. 实现状态提升的三个步骤)
    • [3. 受控组件 vs. 非受控组件](#3. 受控组件 vs. 非受控组件)
    • [4. 单一数据源原则 (Single Source of Truth)](#4. 单一数据源原则 (Single Source of Truth))
    • [5. 状态提升后的组件通信模型](#5. 状态提升后的组件通信模型)
    • [💡 总结语](#💡 总结语)
  • [四、对 state 进行保留和重置](#四、对 state 进行保留和重置)
    • [1. 核心原则:状态的"居住地"](#1. 核心原则:状态的“居住地”)
    • [2. 状态何时会被【保留】?](#2. 状态何时会被【保留】?)
    • [3. 状态何时会被【重置】?](#3. 状态何时会被【重置】?)
    • [4. 如何【强制】重置状态?](#4. 如何【强制】重置状态?)
    • [5. 开发中的重要禁忌](#5. 开发中的重要禁忌)
      • [❌ 严禁嵌套定义组件](#❌ 严禁嵌套定义组件)
    • 总结摘要表
  • [五、迁移状态逻辑至 Reducer 中](#五、迁移状态逻辑至 Reducer 中)
    • [1. 什么是 Reducer?](#1. 什么是 Reducer?)
    • [2. 核心语法对比](#2. 核心语法对比)
      • [useState 模式(分散)](#useState 模式(分散))
      • [useReducer 模式(解耦)](#useReducer 模式(解耦))
    • [3. useState vs useReducer 深度对比](#3. useState vs useReducer 深度对比)
    • [4. 编写 Reducer 的两条"金律"](#4. 编写 Reducer 的两条“金律”)
      • [核心:保持 Reducer 的纯净 (Pure)](#核心:保持 Reducer 的纯净 (Pure))
      • [逻辑:Action 描述"交互"而非"数据更新"](#逻辑:Action 描述“交互”而非“数据更新”)
    • [3. 快速决策指南](#3. 快速决策指南)
  • [六、使用 Context 深层传递参数](#六、使用 Context 深层传递参数)
    • [1. 核心机制:解决"Prop 逐级透传"](#1. 核心机制:解决“Prop 逐级透传”)
    • [2. 使用 Context 的三个标准步骤](#2. 使用 Context 的三个标准步骤)
    • [3. Context 的关键特性](#3. Context 的关键特性)
    • [4. 决策指南:何时使用?](#4. 决策指南:何时使用?)
    • [💡 深度思考:与 CSS 属性继承的类比](#💡 深度思考:与 CSS 属性继承的类比)
  • [七、React 进阶:Reducer + Context 模式总结](#七、React 进阶:Reducer + Context 模式总结)
    • [1. 核心价值:为什么要结合使用?](#1. 核心价值:为什么要结合使用?)
    • [2. 实现的三个关键步骤](#2. 实现的三个关键步骤)
      • [第一步:创建 Context](#第一步:创建 Context)
      • [第二步:提供 Context (Provider)](#第二步:提供 Context (Provider))
      • [第三步:使用 Context (Consumer)](#第三步:使用 Context (Consumer))
    • [3. 最佳实践:模块化封装](#3. 最佳实践:模块化封装)
    • [4. 代码模式对比](#4. 代码模式对比)

一、使用 State 响应输入

1. 命令式 vs 声明式

  • 命令式(Imperative):像给司机下达具体指令:"过两个路口左转,看到红房子停车"。你必须手动操作每一个 DOM 元素(显示、隐藏、禁用)。

  • 声明式(Declarative):像告诉出租车司机:"去机场"。你只需要描述组件在不同状态下看起来是什么样,React 会负责更新 DOM。


2. 声明式地设计 UI 的 5 个步骤

当你准备开发一个交互组件时,请遵循以下流程:

第一步:定位组件中不同的视图状态

像设计师一样,列出用户可能看到的所有视觉状态:

  • Empty: 初始状态,按钮禁用。

  • Typing: 正在输入,按钮启用。

  • Submitting: 提交中,表单禁用,显示加载动画。

  • Success: 提交成功,显示反馈信息。

  • Error: 提交失败,显示错误提示,允许重试。

第二步:确定触发状态改变的因素

  • 人为输入:点击按钮、切换输入框、点击链接。

  • 计算机输入:网络请求成功/失败、定时器结束。

第三步:用 useState 表示内存中的 State

先写下所有可能需要的变量(即使它们看起来有重复):

javascript 复制代码
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
// ... 等等

第四步:删除不必要的 State(精简逻辑)

审查你的 State,问自己三个问题:

  • 是否矛盾?(例如 isTyping 和 isSubmitting 同时为 true 是不可能的,应合并为 status 变量)。

  • 是否重复?(例如 isEmpty 可以通过 answer.length === 0 计算得出)。

  • 是否可以推导?(例如 isError 可以通过 error !== null 得到)。

精简后的结果示例:

javascript 复制代码
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing'); // 'typing', 'submitting', 'success'

第五步:连接事件处理函数

最后,通过事件(如 onChange, onSubmit)调用 set 函数来切换状态。

💡 核心心法总结

  • UI 是状态的反映: U I = f ( s t a t e ) UI = f(state) UI=f(state)。
  • 状态机思维:把组件想象成一个状态机,明确每个状态下的视觉表现以及状态间的迁移路径。
  • 减少"不可能的状态":通过合并相关的变量(如使用字符串状态机而非多个布尔值),避免 UI 出现既在加载又在报错的尴尬情况。

二、选择状态结构

构建良好的 State 结构就像是为建筑搭建稳固的骨架。设计得当的 State 能显著减少 Bug,并使组件更易于修改和调试。

优化目标 解决方案 核心益处
同步更新 合并关联变量为对象 防止更新遗漏
消除冲突 使用状态枚举 (Status) 杜绝"既在加载又在成功"的矛盾
保持简洁 删除冗余/可推导的变量 减少内存占用,逻辑更清晰
数据同步 存储 ID 而非完整对象 确保关联数据永远是最新的
易于维护 扁平化嵌套结构 更新逻辑从 O(N) 降为 O(1)

1. 合并关联的 state

如果某两个 state 变量总是一起变化,则将它们统一成一个 state 变量可能更好。

  • ❌ 差const [x, setX] = useState(0); const [y, setY] = useState(0);
  • ✅ 好const [position, setPosition] = useState({ x: 0, y: 0 }); 提示 :更新对象 State 时,必须使用 setPosition({ ...position, x: 100 }) 展开运算符来显式复制其他属性。

2. 避免矛盾的 state

维度 ❌ 多个布尔值 (容易出错) ✅ 状态枚举 (推荐)
State 声明 const [isSending, setIsSending] = useState(false); const [isSent, setIsSent] = useState(false); const [status, setStatus] = useState('typing');
逻辑维护 手动同步 :开发者必须确保在一个变为 true 时,另一个手动变为 false 自动互斥:只需要切换一个字符串值,天然避免了状态重叠。
可读性 代码中充斥着复杂的布尔组合判断,难以直观一眼看出当前阶段。 通过语义化的状态名(如 'sending')直观判断当前 UI 阶段。
健壮性 可能出现 isSendingisSent 同时为 true 的逻辑 Bug。 状态机模型确保在任何时刻只能处于一种确定的状态。

3. 避免冗余的 state

  • 准则:能算出来的,就别存

  • 坏处:如果你把 fullName 存入 state,你必须在 setFirstName 和 setLastName 的地方手动去更新它,一旦漏掉一个,数据就不同步了。

javascript 复制代码
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState(''); // ❌ 冗余!
  • 正解: 在渲染期间实时计算 const fullName = firstName + ' ' + lastName;。

不要在 state 中镜像 props:

javascript 复制代码
function Message({ colorProp }) {
  const [color, setColor] = useState(colorProp); // 🔴 危险!
}
  • 原因:如果父组件的 colorProp 变了,子组件的 color State 不会随之更新(因为它只在挂载时初始化一次)。

  • 例外:仅当你明确希望忽略后续更新时使用,并建议改名为 initialColor。

4. 避免重复的 state

  • 情景:从一个列表中选择一项。

  • 错误:把选中的整个对象存入 selectedItem。

  • 正确:只存选中项的 id。

javascript 复制代码
const [selectedId, setSelectedId] = useState(0);
const selectedItem = items.find(item => item.id === selectedId);

5:避免深度嵌套的 State

  • 问题:更新一个深层嵌套的地点(如:地球 -> 亚洲 -> 中国 -> 北京)需要复制每一层父级,代码极其复杂。

  • 优化:扁平化(归一化)。像数据库一样,用 ID 索引来存储数据。

三、在组件间共享状态

1. 什么是状态提升?

状态提升是指将原本在子组件中各自维护的 state 移动到它们最近的公共父组件上。

  • 目的:让多个子组件能够共享同一个数据源,实现状态的同步更改。
  • 实现方式 :父组件保存 state,并通过 props 将数据和修改数据的函数(回调函数)传递给子组件。

2. 实现状态提升的三个步骤

当你发现两个组件需要同步变化时,可以按以下节奏重构:

  1. 从子组件中移除 state :删除子组件内部的 useState,改为从 props 接收数据。
  2. 从父组件传递硬编码数据 :先在父组件中通过 props 给子组件传一个固定的值,确保子组件能根据 props 正确渲染。
  3. 在父组件添加真正的 state
    • 在父组件定义 useState
    • state 变量作为 props 传给子组件。
    • 将改变 state 的函数(如 setActiveIndex)封装成事件处理程序(如 onShow)传给子组件。

3. 受控组件 vs. 非受控组件

这是一个重要的架构概念:

类型 驱动源 特点 灵活性
非受控组件 内部 state 组件自己管理状态,父组件难以干预。 较低(难以与其他组件同步)
受控组件 外部 props 组件的行为完全由父组件驱动。 较高(容易组合和协调)

提示:在实际开发中,组件往往是混合的。有些 UI 交互(如 CSS 悬停效果)适合非受控,而核心业务数据(如表单值、当前激活项)通常推荐受控。


4. 单一数据源原则 (Single Source of Truth)

  • 定义 :对于应用中的每一个状态,都应该由唯一一个组件来负责"掌控"它。
  • 原则 :不要在多个组件中复制相同的状态,而是通过 props 向下流动,或者通过状态提升向上移动。
  • 动态性:状态的位置不是固定的。随着功能增加,状态可能会在组件树中上下移动,这是 React 开发中非常自然的重构过程。

5. 状态提升后的组件通信模型

  • 向下通信(数据驱动) :父组件通过 propsstate 传给子组件,子组件被动渲染。
  • 向上通信(行为触发) :父组件通过 props 传给子组件一个回调函数 。当子组件发生交互(如点击按钮)时,调用该函数,通知父组件修改 state

💡 总结语

状态提升是解决 React 组件间"步调不一致"的万金油。当你犹豫状态该放哪时,问自己一句:"谁需要知道这个状态?" 如果有两个兄弟组件都需要,那就把它提到它们的公共父级那里去。

四、对 state 进行保留和重置

这份教程深入浅出地解释了 React 中 State 的生命周期与 UI 树位置之间的核心关系。


1. 核心原则:状态的"居住地"

  • 状态不在 JSX 里 :虽然你在组件内部编写 useState,但 State 实际上是由 React 内部保存的。
  • 关联依据(位置) :React 根据组件在 UI 渲染树中的位置,将保存的状态与对应的组件关联起来。
  • 位置即"地址":你可以把 UI 树中的层级结构看作组件的"地址"。只要地址没变,即使父组件更新了,状态通常也会保留。

2. 状态何时会被【保留】?

只要满足以下 "双同"条件,状态就会被保留:

  1. 相同的位置:在父组件的子节点顺序/结构中处于同一层级。
  2. 相同的组件类型 :标签名(如 <Counter />)没有发生改变。

⚠️ 陷阱提醒 :React 关心的是渲染树中的最终结果,而不是你代码里的 if/else 逻辑。即使你在不同的 return 分支里写了两个 <Counter />,只要它们最终渲染在树的同一位置,React 就会认为它们是同一个实例。


3. 状态何时会被【重置】?

当以下任一情况发生时,React 会销毁旧组件及其所有子树的状态:

  • 组件被移除 :组件从渲染树中消失(例如条件渲染为 false)。
  • 位置发生了不同类型的组件切换 :比如同一位置从 <Counter /> 变成了 <p>
  • 父级结构改变 :即使组件本身没变,但它的父标签变了(例如从被 <div> 包裹变成被 <section> 包裹)。

4. 如何【强制】重置状态?

有时即使组件位置没变,我们也希望清除状态(例如切换聊天对象时清空输入框)。

  1. 方法一:改变组件位置(不推荐)
    • 通过逻辑让两个组件渲染在不同的层级位置,但这会增加 JSX 的复杂度。
  2. 方法二:使用 key(推荐)
    • 身份标识key 不仅仅用于列表。给组件一个唯一的 key(如 key={userId}),可以告诉 React:"即使我在同一个位置,但我是一个全新的组件"。
    • 效果 :当 key 改变时,React 会销毁旧的组件实例(及其状态)并创建一个全新的组件。

5. 开发中的重要禁忌

❌ 严禁嵌套定义组件

错误做法: 不要在函数组件 A 的内部定义函数组件 B。

  • 后果:每次 A 重新渲染时,都会创建一个全新的 B 函数(类型引用变了)。
  • 现象:这会导致 B 在每次父组件更新时都会彻底重置状态,引发输入框失去焦点、性能下降等严重 Bug。

总结摘要表

场景 React 的行为 结果
位置相同 + 类型相同 保留状态 状态延续
位置不同 销毁旧状态,初始化新状态 状态重置
位置相同 + 类型改变 销毁旧状态,初始化新状态 状态重置
位置相同 + 类型相同 + Key 改变 视为不同组件 状态重置

五、迁移状态逻辑至 Reducer 中

当组件的状态更新逻辑变得复杂且分散时,使用 useReducer 可以将逻辑整合到组件外部的一个统一函数中(即 Reducer),从而提高代码的可读性和可维护性。


1. 什么是 Reducer?

Reducer 是处理状态的一种新方式。它将组件中"发生了什么"(Action)与"状态如何更新"(State Logic)分离开来。

迁移的三大步骤:

  1. Dispatch Action:在事件处理程序中,不再直接设置状态,而是派发(dispatch)一个描述用户操作的 action 对象。
  2. 编写 Reducer 函数:编写一个外部函数,根据不同的 Action 类型返回新的状态。
  3. 在组件中使用 :使用 useReducer Hook 替换 useState

2. 核心语法对比

useState 模式(分散)

javascript 复制代码
function handleAddTask(text) {
  setTasks([...tasks, { id: nextId++, text, done: false }]);
}

function handleChangeTask(task) {
  setTasks(
    tasks.map((t) => {
      if (t.id === task.id) {
        return task;
      } else {
        return t;
      }
    })
  ); 
}

function handleDeleteTask(taskId) {
  setTasks(tasks.filter((t) => t.id !== taskId));
}

useReducer 模式(解耦)

第一步:派发 Action

javascript 复制代码
function handleAddTask(text) {
  dispatch({
    type: 'added',
    id: nextId++,
    text: text,
  });
}

function handleChangeTask(task) {
  dispatch({
    type: 'changed',
    task: task,
  });
}

function handleDeleteTask(taskId) {
  dispatch({
    type: 'deleted',
    id: taskId,
  });
}

第二步:编写 Reducer (通常放在组件外部)

javascript 复制代码
function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [...tasks, { id: action.id, text: action.text, done: false }];
    }
    case 'changed': {
      return tasks.map(t => (t.id === action.task.id ? action.task : t));
    }
    case 'deleted': {
      return tasks.filter(t => t.id !== action.id);
    }
    default: {
      throw Error('未知 action: ' + action.type);
    }
  }
}

第三步:在组件中使用

javascript 复制代码
import { useReducer } from 'react';

const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

3. useState vs useReducer 深度对比

维度 useState useReducer
代码体积 初始代码少。非常适合管理简单的布尔值、数字或字符串。 初始代码较多。需要提前定义 Reducer 函数和各种 Action 类型。
可读性 当更新逻辑变得复杂时,处理程序会变得臃肿,可读性下降 将逻辑从组件中抽离,实现关注点分离,逻辑层级更清晰。
调试体验 出现问题时,很难追踪是哪个函数在何时修改了状态。 极其强大。通过打印 Action 日志,可以清晰看到每一个用户交互序列。
测试便利性 逻辑耦合在组件内,通常需要配合组件挂载进行集成测试。 极易测试。Reducer 是纯函数,不依赖 React 环境,可直接进行单元测试。

4. 编写 Reducer 的两条"金律"

为了确保应用的可预测性和易维护性,在编写 Reducer 时必须遵循以下原则:

核心:保持 Reducer 的纯净 (Pure)

  • 无副作用:不要在 Reducer 内部发送 API 请求、执行定时器或进行任何影响外部的操作。
  • 不可变更新 :永远不要直接修改传入的 state。必须通过展开运算符 ... 或数组方法返回一个全新的对象/数组副本
  • 确定性:同样的输入(State + Action)必须永远得到同样的输出。

逻辑:Action 描述"交互"而非"数据更新"

  • 单一交互原则 :一个 Action 应该对应一个具体的用户行为
  • 示例 :如果用户点击"重置表单",应该派发一个 type: 'reset_form'
    • 错误做法 :连续派发 5 个 set_field 的 Action。
    • 正确做法 :派发 1 个 reset_form Action,由 Reducer 一次性将 5 个字段全部重置。
    • 好处:调试日志会非常清晰,一眼就能看出用户点击了重置按钮。

3. 快速决策指南

  • 使用 useState :状态是独立的(如 isOpen)、逻辑简单、只有 1-2 个地方会修改它。
  • 使用 useReducer:状态逻辑复杂(多个状态相互关联)、多个事件处理程序以相似方式修改状态、或者需要维护大型对象/数组。

六、使用 Context 深层传递参数

Context 提供了一种在组件树中传递数据的方法,无需在每个层级手动传递 props。


1. 核心机制:解决"Prop 逐级透传"

在 React 中,数据流通常是自上而下的(单向数据流)。

  • 痛点(Prop Drilling):当深层子组件需要祖先组件的数据时,必须通过中间组件一层层手动传递 Props,导致代码冗长且难以维护。
  • 对策(Context):Context 建立了一个"广播系统",允许数据绕过中间组件,直接从祖先组件"直达"任何深层的后代组件。

2. 使用 Context 的三个标准步骤

实现 Context 就像配置一个无线广播电台:

步骤 动作 关键语法
Step 1: 建立电台 创建并导出 Context 对象 export const MyContext = createContext(defaultValue);
Step 2: 接收信号 在子组件内通过 Hook 读取 const value = useContext(MyContext);
Step 3: 发射信号 在父组件中提供数据 <MyContext value={yourValue}> {children} </MyContext>

💡 版本注意 :在 React 最新版本中,可以直接使用 <MyContext value={...}>;旧版本则需要使用 <MyContext.Provider value={...}>


3. Context 的关键特性

  • 就近原则 :子组件总是从 UI 树中离它最近的那个 Provider 获取值。
  • 动态响应 :Context 的值通常与 state 绑定。当 Provider 的 value 改变时,所有使用了 useContext 的子组件都会自动重新渲染
  • 强穿透力 :Context 可以穿过任何中间组件(即使中间组件是静态的或使用了 React.memo),不会被中断。
  • 高度独立:不同的 Context 互不干扰。一个组件可以轻松消费多个不同的 Context(如同时读取"主题"和"用户信息")。

4. 决策指南:何时使用?

Context 虽然强大,但会使组件产生耦合(降低复用性),应谨慎使用。

优先考虑的替代方案

  1. 继续传递 Props:如果层级较浅,Props 是最清晰、最显式的数据流方式。
  2. 组件组合 (Children) :通过 children 属性将子组件传入,利用"内容分发"减少中间传递层级。

最佳实践场景

  • 全局偏好:如深色/浅色模式(Theme)、语言国际化(i18n)。
  • 登录状态:当前登录的用户信息、权限验证。
  • 路由/状态库:许多库(如 React Router, Redux)底层都基于 Context 实现。
  • 复杂组件通信:如折叠面板(Accordion)或选项卡(Tabs),父容器与深层子项共享交互状态。

💡 深度思考:与 CSS 属性继承的类比

Context 的工作方式非常类似于 CSS 的 属性继承 (如 colorfont-family):

  • 你在根节点设置了 color: blue,所有子元素默认都变蓝。
  • 如果你在中间某个容器设置了 color: green,该容器内部的所有子元素都会"覆盖"掉蓝色,变为绿色。

这正是 Context 的精髓:让组件能够"适应周围环境",并根据所处的 Context 渲染出不同的形态。

七、React 进阶:Reducer + Context 模式总结

useReducer 的状态管理逻辑与 Context API 的跨层级传递能力相结合,是 React 中管理中大型应用状态的黄金搭档。


1. 核心价值:为什么要结合使用?

  • 解决 Props 钻取(Props Drilling) :避免将 statedispatch 像接力棒一样穿过数十个中间组件,只为了传给最深层的子组件。
  • 清晰的职责分离
    • Reducer:定义"如何更新状态"(逻辑中心)。
    • Context:定义"数据传给谁"(广播中心)。
  • 简化组件维护:中间组件不再需要关心不属于它们的数据,代码更整洁。

2. 实现的三个关键步骤

第一步:创建 Context

通常建议创建 两个独立的 Context,以优化性能:

  1. TasksContext:传递当前的状态数据(State)。
  2. TasksDispatchContext:传递更新状态的函数(Dispatch)。

注意:分开存放可以确保那些只发送 Action 而不读取数据的组件,在数据变化时不会被强制重新渲染。

第二步:提供 Context (Provider)

在顶层组件中:

  • 调用 useReducer 获取 tasksdispatch
  • 使用嵌套的 Provider 将这两个值注入到组件树中。

第三步:使用 Context (Consumer)

深层组件通过 Hook 获取所需资源:

  • useContext(TasksContext) 获取数据。
  • useContext(TasksDispatchContext) 获取派发函数。

3. 最佳实践:模块化封装

为了提高可重用性和安全性,通常将逻辑封装在独立文件中(如 TasksContext.js):

  • 封装 Provider 组件 :创建一个 TasksProvider,内部管理 useReducer,外部包裹 {children}
  • 自定义 Hook
    • 导出 useTasks()useTasksDispatch()
    • 优势:组件代码更简洁,且可以在 Hook 中加入错误处理(例如:检查 Hook 是否在 Provider 外部被非法调用)。

4. 代码模式对比

特性 仅使用 Reducer Reducer + Context 模式
状态存储 存在于顶层组件 存在于独立的 Provider 内部
数据传递 依靠 Props 逐层传递 通过 Context 跨层级"瞬移"
组件耦合 子组件强依赖父组件传入的 Props 子组件直接消费全局/局部 Context
适用场景 简单、层级浅的组件结构 复杂、层级深、状态共享频繁的应用

!TIP

设计思想

这种模式是 Redux 等主流状态管理库的核心灵魂。掌握了它,你就理解了单向数据流和集中式状态管理的精髓,未来迁移到 Redux 或 Zustand 将会非常轻松。

相关推荐
花归去1 小时前
a-table 冻结列导致边框
前端·css·css3
kuuailetianzi1 小时前
Vue 3图片全屏预览组件:打造专业级图像浏览体验
前端·javascript·microsoft
前端杂货铺2 小时前
manifold-3d——在 Vue 项目中实现干涉检查
前端·vue.js·manifold
苦 涩2 小时前
考研408笔记之操作系统(五)——输入输出(IO)管理
笔记·操作系统·考研408
恋猫de小郭2 小时前
Bun 官方将正式支持 Android,Claude Code 未来可以直接在手机上跑
android·前端·ai编程
晓得迷路了2 小时前
栗子前端技术周刊第 126 期 - Rspack 2.0、TypeScript 7.0 Beta、Git 2.54...
前端·javascript·ai编程
小小码农Come on2 小时前
单例 QtObject 全局配置
开发语言·前端·javascript
摸鱼仙人~2 小时前
HTTP状态码全量详解(定义+核心区别+业务场景+前端常见诱因+排查方案+工程处理)
前端·网络协议·http
他是龙5512 小时前
DVWA SQL 注入全级别通关笔记(Low / Medium / High / Impossible)
数据库·笔记·sql