【React】React学习:从初级到高级(三)
3 状态管理
随着应用不断变大,应该更有意识的去关注应用状态如何组织,以及数据如何在组件之间流动。冗余或重复的状态往往是缺陷的根源。
3.1 用State响应输入
3.1.1 声明式地考虑UI
总体步骤如下:
-
定位组件中不同的视图状态
-
确定是什么触发了这些 state 的改变
- 人为输入,人为输入一般需要事件处理函数
- 计算机输入
-
表示内存中的 state(需要使用
useState
)- 需要让“变化的部分”尽可能的少,可以先从必须的状态开始
- 刚开始若不确定可以设置多一些视图状态,之后不断尝试重构state
-
删除任何不必要的 state 变量
-
这个 state 是否会导致矛盾?
-
相同的信息是否已经在另一个 state 变量中存在?
-
你是否可以通过另一个 state 变量的相反值得到相同的信息?
Reducer 可以合并多个状态变量到一个对象中并巩固所有相关的逻辑!
-
-
连接事件处理函数去设置 state
- 创建事件处理函数去设置 state 变量。
同时展示大量的视图状态,这样的页面通常被称作
living styleguide
或者storybook
挑战:这个表单在两种模式间切换:编辑模式,你可以看到输入框;查看模式,你只能看到结果。按钮的标签会根据你所处的模式在“编辑”和“保存”两者中切换。当你改变输入框的内容时,欢迎信息会最下面实时更新。
import {useState} from 'react';export default function EditProfile() {const [display, setDisplay] = useState(false);const [person, setPerson] = useState({firstName: 'Joe',lastName: 'Stan'});function handleFirstNameChange(e) {setPerson({...person,[e.target.name]: e.target.value})}function handleLastNameChange(e) {setPerson({...person,[e.target.name]: e.target.value})}return (<form onSubmit={e => {e.preventDefault(),setDisplay(!display)}}><label>First name:{' '}{display ? <input name="firstName"value={person.firstName}onChange={handleFirstNameChange} /> :<b>{person.firstName}</b>}</label><label>Last name:{' '}{display ? <input name="lastName" value={person.lastName}onChange={handleLastNameChange} /> :<b>{person.lastName}</b>}</label><button type="submit">{display ? 'Save' : 'Edit'} Profile</button><p><i>Hello, {person.firstName} {person.lastName}!</i></p></form>);
}
updateDOM
函数展示了当你设置 state 时,React 在幕后都做了什么.
// index.js
let firstName = 'Jane';
let lastName = 'Jacobs';
let isEditing = false;function handleFormSubmit(e) {e.preventDefault();setIsEditing(!isEditing);
}function handleFirstNameChange(e) {setFirstName(e.target.value);
}function handleLastNameChange(e) {setLastName(e.target.value);
}function setFirstName(value) {firstName = value;updateDOM();
}function setLastName(value) {lastName = value;updateDOM();
}function setIsEditing(value) {isEditing = value;updateDOM();
}function updateDOM() {if (isEditing) {editButton.textContent = 'Save Profile';hide(firstNameText);hide(lastNameText);show(firstNameInput);show(lastNameInput);} else {editButton.textContent = 'Edit Profile';hide(firstNameInput);hide(lastNameInput);show(firstNameText);show(lastNameText);}firstNameText.textContent = firstName;lastNameText.textContent = lastName;helloText.textContent = ('Hello ' +firstName + ' ' +lastName + '!');
}function hide(el) {el.style.display = 'none';
}function show(el) {el.style.display = '';
}let form = document.getElementById('form');
let editButton = document.getElementById('editButton');
let firstNameInput = document.getElementById('firstNameInput');
let firstNameText = document.getElementById('firstNameText');
let lastNameInput = document.getElementById('lastNameInput');
let lastNameText = document.getElementById('lastNameText');
let helloText = document.getElementById('helloText');
form.onsubmit = handleFormSubmit;
firstNameInput.oninput = handleFirstNameChange;
lastNameInput.oninput = handleLastNameChange;
/* index.html */
<form id="form"><label>First name:<b id="firstNameText">Jane</b><inputid="firstNameInput"value="Jane"style="display: none"></label><label>Last name:<b id="lastNameText">Jacobs</b><inputid="lastNameInput"value="Jacobs"style="display: none"></label><button type="submit" id="editButton">Edit Profile</button><p><i id="helloText">Hello, Jane Jacobs!</i></p>
</form><style>
* { box-sizing: border-box; }
body { font-family: sans-serif; margin: 20px; padding: 0; }
label { display: block; margin-bottom: 20px; }
</style>
3.2 选择state结构
3.1.1 构建state的原则
-
合并关联的 state
- 如果某两个 state 变量总是一起变化,则将它们统一成一个 state 变量可能更好
- 另一种情况是,将数据整合到一个对象或一个数组中时,不知道需要多少个 state 片段
-
避免互相矛盾的 state
-
避免冗余的 state
-
“镜像”一些 prop 属性会导致混淆,建议使用常量
function Message({ messageColor }) {const color = messageColor;
只有当想要 忽略特定 props 属性的所有更新时,将 props “镜像”到 state 才有意义。按照惯例,prop 名称以
initial
或default
开头,以阐明该 prop 的新值将被忽略:function Message({ initialColor }) {// 这个 `color` state 变量用于保存 `initialColor` 的 **初始值**。// 对于 `initialColor` 属性的进一步更改将被忽略。const [color, setColor] = useState(initialColor);
-
-
避免重复的 state
-
避免深度嵌套的 state
3.3 在组件间共享状态(状态提升)
当编写一个组件时,你应该考虑哪些信息应该由父组件控制(通过传递 props
),哪些信息应该由内部state
控制(通过 state
)。
进行状态提升的步骤:
- 从子组件中 移除 state 。
- 从父组件 传递 props。
- 为子组件的共同父组件添加 state ,并将其与事件处理函数一起向下传递
3.4 对state进行保留和重置
React 使用树形结构来对开发者创造的 UI 进行管理和建模。
3.4.1 state与树中的某个位置相关联
根据组件在 UI 树中的位置,React 将它所持有的每个 state 与正确的组件关联起来。 React 在移除一个组件时,也会销毁它的 state。下面是一个组件的添加与删除。
只要一个组件还被渲染在 UI 树的相同位置,React 就会保留它的 state。 如果它被移除,或者一个不同的组件被渲染在相同的位置,那么 React 就会丢掉它的 state。
3.4.2 相同位置的相同组件会保留state
更新 App
的状态不会重置 Counter
,因为 Counter
始终保持在同一位置。
import { useState } from 'react';export default function App() {const [isFancy, setIsFancy] = useState(false);return (<div>{isFancy ? (<Counter isFancy={true} /> ) : (<Counter isFancy={false} /> )}<label><inputtype="checkbox"checked={isFancy}onChange={e => {setIsFancy(e.target.checked)}}/>使用好看的样式</label></div>);
}function Counter({ isFancy }) {const [score, setScore] = useState(0);const [hover, setHover] = useState(false);let className = 'counter';if (hover) {className += ' hover';}if (isFancy) {className += ' fancy';}return (<divclassName={className}onPointerEnter={() => setHover(true)}onPointerLeave={() => setHover(false)}><h1>{score}</h1><button onClick={() => setScore(score + 1)}>加一</button></div>);
}
对 React 来说重要的是组件在 UI 树中的位置,而不是在 JSX 中的位置!
3.4.3 相同位置的不同组件会重置state
import { useState } from 'react';export default function App() {const [isPaused, setIsPaused] = useState(false);return (<div>{isPaused ? (<p>待会见!</p> ) : (<Counter /> )}<label><inputtype="checkbox"checked={isPaused}onChange={e => {setIsPaused(e.target.checked)}}/>休息一下</label></div>);
}function Counter() {const [score, setScore] = useState(0);const [hover, setHover] = useState(false);let className = 'counter';if (hover) {className += ' hover';}return (<divclassName={className}onPointerEnter={() => setHover(true)}onPointerLeave={() => setHover(false)}><h1>{score}</h1><button onClick={() => setScore(score + 1)}>加一</button></div>);
}
当 Counter
变为 p
时,Counter
会被移除,同时 p
被添加。
当切换回来时,p
会被删除,而 Counter
会被添加。
刚开始 <div>
的第一个子组件是一个 Counter
。但是当切换成 p
时,React 将 Counter
从 UI 树中移除了并销毁了它的状态。
当在相同位置渲染不同的组件时,组件的整个子树都会被重置
当 section
变为 div
时,section
会被删除,新的 div
被添加
当切换回来时,div
会被删除,新的 section
被添加。
如果想在重新渲染时保留 state,几次渲染中的树形结构就应该相互“匹配”。
3.4.4 在相同位置重置相同组件的state
- 方法1:将组件渲染在不同位置(适用于只有少数几个组件)
-
方法2:使用
key
赋予每个组件一个明确的身份{isPlayerA ? (<Counter key="Taylor" person="Taylor" /> ) : (<Counter key="Sarah" person="Sarah" /> )}
请记住 key 不是全局唯一的。它们只能指定 父组件内部 的顺序。
3.4.5 使用key
重置表单
给Chat
组件添加一个 key
,就可以保证每次选择一个不同的收件人时,Chat
组件包括其下方树中的state
就会被重新创建。
<Chat key={to.id} contact={to} />
3.4.6 为被移除的组件保留state
比如聊天应用
- 如果UI比较简单,就可以使用CSS把其他聊天隐藏起来
- 进行状态提升,让父组件保存信息
- 使用其他数据源
3.5 迁移状态逻辑至Reducer中
3.5.1 使用reducer整合状态逻辑
Reducer 是处理状态的另一种方式。你可以通过三个步骤将 useState
迁移到 useReducer
:
-
将设置状态的逻辑 修改 成
dispatch
的一个action
;比如下面这段代码:
function handleAddTask(text) {setTasks([...tasks,{id: nextId++,text: 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)); }
移除所有状态设置逻辑,只留下三个事件处理函数。通过事件处理函数
dispatch
一个action
来指明 “用户刚刚做了什么”。(状态更新逻辑则保存在其他地方!),修改后的代码如下:function handleAddTask(text) {dispatch(// action 对象{type: 'added',id: nextId++,text: text,}); }function handleChangeTask(task) {dispatch({type: 'changed',task: task,}); }function handleDeleteTask(taskId) {dispatch({type: 'deleted',id: taskId,}); }
action 对象可以有多种结构。
按照惯例,通常会添加一个字符串类型的
type
字段来描述发生了什么,并通过其它字段传递额外的信息。type
是特定于组件的,在这个例子中added
和addded_task
都可以。选一个能描述清楚发生的事件的名字!dispatch({// 针对特定的组件type: 'what_happened',// 其它字段放这里 });
-
编写 一个
reducer
函数;reducer 函数就是你放置状态逻辑的地方。它接受两个参数,分别为当前 state 和 action 对象,并且返回的是更新后的 state:
function yourReducer(state, action) {// 给 React 返回更新后的状态 }
在这个例子中,要将状态设置逻辑从事件处理程序移到 reducer 函数中,你需要:
- 声明当前状态(
tasks
)作为第一个参数; - 声明
action
对象作为第二个参数; - 从
reducer
返回 下一个 状态(React 会将旧的状态设置为这个最新的状态)。
function tasksReducer(tasks, action) {if (action.type === 'added') {return [...tasks,{id: action.id,text: action.text,done: false,},];} else if (action.type === 'changed') {return tasks.map((t) => {if (t.id === action.task.id) {return action.task;} else {return t;}});} else if (action.type === 'deleted') {return tasks.filter((t) => t.id !== action.id);} else {throw Error('未知 action: ' + action.type);} }
上面语句用了
if/else
语句,但在reducers
中使用switch
语句更加一目了然: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) => {if (t.id === action.task.id) {return action.task;} else {return t;}});}case 'deleted': {return tasks.filter((t) => t.id !== action.id);}default: {throw Error('未知 action: ' + action.type);}} }
- 声明当前状态(
-
在组件中 使用
reducer
。
事件处理程序只通过派发 action
来指定 发生了什么,而 reducer
函数通过响应 actions
来决定 状态如何更新。
3.5.2 对比useState和useReducer
- 代码体积:UI组件少可以用
useState
,组件复杂用useReducer
- 可读性:依据状态更新逻辑复杂程度而定,简单时
useState
,复杂时useReducer
- 可调试性:
useReducer
更佳,可以通过打印日志来调试 - 可测试性:
reducer
函数是一个纯函数,可以单独进行测试
3.5.3 编写一个好的reducer函数
reducer
必须是一个纯函数- 每个
action
都描述了一个单一的用户交互,即便它会引发多个数据的变化
3.5.4 使用Immer简化reducers
引入useImmerReducer
:
import { useImmerReducer } from 'use-immer';
使用draft.函数
修改state:
function tasksReducer(draft, action) {switch (action.type) {case 'added': {draft.push({id: action.id,text: action.text,done: false,});break;}case 'changed': {const index = draft.findIndex((t) => t.id === action.task.id);draft[index] = action.task;break;}case 'deleted': {return draft.filter((t) => t.id !== action.id);}default: {throw Error('未知 action:' + action.type);}}
}
3.6 使用Context深层传递参数
Context 允许父组件向其下层无论多深的任何组件提供信息,而无需通过 props 显式传递。
-
创建context
// LevelContext.js import { createContext } from 'react';export const LevelContext = createContext(1);
-
使用context
import { useContext } from 'react'; import { LevelContext } from './LevelContext.js';export default function Heading({ children }) {const level = useContext(LevelContext);// ... }
-
提供context
用 context provider 包裹起来 以提供
LevelContext
给它们:import { LevelContext } from './LevelContext.js';export default function Section({ level, children }) {return (<section className="section"><LevelContext.Provider value={level}>{children}</LevelContext.Provider></section>); }
Context 让你可以编写“适应周围环境”的组件,并且根据在哪 (或者说 在哪个 context 中)来渲染它们不同的样子。不同的 React context 不会覆盖彼此。Context 会穿过中间的任何组件。
3.6.1 context的使用场景
- 主题: 如果你的应用允许用户更改其外观(例如暗夜模式),你可以在应用顶层放一个
context provider
,并在需要调整其外观的组件中使用该 context。 - 当前账户: 许多组件可能需要知道当前登录的用户信息。将它放到 context 中可以方便地在树中的任何位置读取它。
- 路由: 大多数路由解决方案在其内部使用 context 来保存当前路由。
- **状态管理:**通常 将
reducer
与context
搭配使用来管理复杂的状态并将其传递给深层的组件来避免过多的麻烦。
3.7 使用Reducer和Context拓展应用
步骤:
- 创建 context。
- 将 state 和 dispatch 放入 context。
- 在组件树的任何地方 使用 context。
为子组件提供 state 和 dispatch 函数:
-
创建两个 context (一个用于 state,一个用于 dispatch 函数)。
import { createContext } from 'react';export const TasksContext = createContext(null); export const TasksDispatchContext = createContext(null);
-
让组件的 context 使用 reducer。
export function TasksProvider({ children }) {const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);return (<TasksContext.Provider value={tasks}><TasksDispatchContext.Provider value={dispatch}>{children}</TasksDispatchContext.Provider></TasksContext.Provider>); } // 也可以从 TasksContext.js 中导出使用 context 的函数: export function useTasks() {return useContext(TasksContext); }export function useTasksDispatch() {return useContext(TasksDispatchContext); }
-
使用组件中需要读取的 context。
// 组件可以通过以下函数读取 context: const tasks = useTasks(); const dispatch = useTasksDispatch();
像
useTasks
和useTasksDispatch
这样的函数被称为自定义 Hook, 如果你的函数名以use
开头,它就被认为是一个自定义 Hook。
相关文章:

【React】React学习:从初级到高级(三)
3 状态管理 随着应用不断变大,应该更有意识的去关注应用状态如何组织,以及数据如何在组件之间流动。冗余或重复的状态往往是缺陷的根源。 3.1 用State响应输入 3.1.1 声明式地考虑UI 总体步骤如下: 定位组件中不同的视图状态 确定是什么…...
Rest和Http什么关系?
分析&回答 REST 定义了一组体系架构原则,您可以根据这些,包括使用不同语言编写的客户端如何通过 HTTP 处理和传输资源状态。 REST只是一种风格,不是一种标准REST是以资源为中心的 用不同的 HTTP 请求方法来处理对资源的 CRUD࿰…...
leetcode原题: 生存人数
题目: 给定 N 个人的出生年份和死亡年份,第 i 个人的出生年份为 birth[i],死亡年份为 death[i],实现一个方法以计算生存人数最多的年份。 你可以假设所有人都出生于 1900 年至 2000 年(含 1900 和 2000 )…...

K8S的介绍和架构
仅供入门 K8S的介绍和架构 一. 什么是kubernetes二、Kubernetes架构和组件 2.1 核心组件 2.1.1 Kubernetes Master控制组件,调度管理整个系统(集群),包含如下组件: a、Kubernetes API Serverb、Kubernetes Schedulerc、Kubernet…...

linux信号量
通过学习linux的信号量,对linux的信号量进行了编程。...

Jupyter Notebook 好用在哪?
Jupyter Notebook 是一个 Web 应用程序,便于创建和共享文学化程序文档,支持实时代码、数学方程、可视化和 Markdown,其用途包括数据清理和转换、数值模拟、统计建模、机器学习等等。目前,数据挖掘领域中最热门的比赛 Kaggle 里的资…...

华为云云服务器评测|基于云服务器的minio部署手册
华为云云服务器评测|基于云服务器的minio部署手册 【软件安装版本】【集群安装(是)(否)】 版本 创建人 修改人 创建时间 备注 1.0 jz jz 2023.9.2 minio华为云耀服务器 一. 部署规划与架构 1. 规…...
【网络安全带你练爬虫-100练】第22练:数据包中参数提取与处理
目录 一、目标1:GET数据包的处理 1、GET数据包中参数的提取 2、GET请求中 统计参数个数 二、目标2:POST数据包的处理 1、post中参数个数的提取 2、POST请求中 统计参数个数 一、目标1:GET数据包的处理 1、GET数据包中参数的提取 impo…...

第64步 深度学习图像识别:多分类建模误判病例分析(Pytorch)
基于WIN10的64位系统演示 一、写在前面 上期我们基于TensorFlow环境介绍了多分类建模的误判病例分析。 本期以健康组、肺结核组、COVID-19组、细菌性(病毒性)肺炎组为数据集,基于Pytorch环境,构建SqueezeNet多分类模型…...

ES查询报错内容长度超过104857600
项目场景: 使用 ElasticsearchRestTemplate 或者使用 RestHighLevelClient 查询 ES 报错 内容长度超过 104857600 问题描述 ES 查询报错 entiity content is too long xxx for the configured buffer limit 104857600 Overridepublic void esQuery() {restHighL…...

2023欧亚合作发展大会暨国际公共采购大会在京举行
2023年9月2日至6日,以“合作、协同、共赢、共享”为主题的“2023欧亚合作发展大会暨国际公共采购大会等系列会议”在北京炎黄书院隆重举行,共有500多位中外贵宾参加了本次盛会。 本次大会指导单位是中国联合国采购促进会、北京市中医药局,由中…...
宝塔面板linux在终端使用命令开启服务保持服务不关闭
我们经常在宝塔面板终端开启服务(比如socket等服务时),如果关闭面板标签页或者关闭终端,服务也随之关闭了,要保持服务一直运行,就需要把终端进程放在linux后台执行,方法如下: 1、先…...

面试题--从键盘输入网站到网页显示,之间发生了什么
文章目录 首先进入HTTP阶段协议栈阶段TCP阶段IP阶段MAC网卡交换机路由器抵达 首先进入HTTP阶段 1.解析对应的URL,访问一个对应的服务器xxx.com的一个文件index.html; 2 使用DNS查询对应的ip地址,通过DNS服务器进行查找 3 组装http报文,生成h…...
字节9.3秋招研发笔试 【后端方向】第三题
题目 小红拿到了一个无向图,初始每人节点是白色,其中有若干个节点被染成了红色。小红想知道,若将 i 号节点染成红色,当前的红色连块的数量是多少? 你需要回答i∈[1,n] 的答案。 定义,若干节点组成一个红色连通块&am…...

Solidity 小白教程:8. 变量初始值
Solidity 小白教程:8. 变量初始值 变量初始值 在solidity中,声明但没赋值的变量都有它的初始值或默认值。这一讲,我们将介绍常用变量的初始值。 值类型初始值 boolean: falsestring: “”int: 0uint: 0enum: 枚举中的第一个元素address: …...

时序预测 | MATLAB实现EEMD-SSA-LSTM、EEMD-LSTM、SSA-LSTM、LSTM时间序列预测对比
时序预测 | MATLAB实现EEMD-SSA-LSTM、EEMD-LSTM、SSA-LSTM、LSTM时间序列预测对比 目录 时序预测 | MATLAB实现EEMD-SSA-LSTM、EEMD-LSTM、SSA-LSTM、LSTM时间序列预测对比预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 时序预测 | MATLAB实现EEMD-SSA-LSTM、E…...

京东搜索EE链路演进 | 京东云技术团队
导读 搜索系统中容易存在头部效应,中长尾的优质商品较难获得充分的展示机会,如何破除系统的马太效应,提升展示结果的丰富性与多样性,助力中长尾商品成长是电商平台搜索系统的一个重要课题。其中,搜索EE系统在保持排序…...

【C++】反向迭代器精讲(以lIst为例)
目录 二,全部代码 三,设计思路 1. 讨论 2. 关于迭代器文档一个小细节 结语 一,前言 如果有小伙伴还未学习普通迭代器,请参考这篇文章中的普通迭代器实现。 【STL】list用法&试做_底层实现_花果山~~程序猿的博客-CSDN…...

时序预测 | MATLAB实现基于PSO-GRU、GRU时间序列预测对比
时序预测 | MATLAB实现基于PSO-GRU、GRU时间序列预测对比 目录 时序预测 | MATLAB实现基于PSO-GRU、GRU时间序列预测对比效果一览基本描述程序设计参考资料 效果一览 基本描述 MATLAB实现基于PSO-GRU、GRU时间序列预测对比。 1.MATLAB实现基于PSO-GRU、GRU时间序列预测对比&…...

2023年高教社杯 国赛数学建模思路 - 案例:感知机原理剖析及实现
文章目录 1 感知机的直观理解2 感知机的数学角度3 代码实现 4 建模资料 # 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 感知机的直观理解 感知机应该属于机器学习算法中最简单的一种算法,其…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...

Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

PH热榜 | 2025-06-08
1. Thiings 标语:一套超过1900个免费AI生成的3D图标集合 介绍:Thiings是一个不断扩展的免费AI生成3D图标库,目前已有超过1900个图标。你可以按照主题浏览,生成自己的图标,或者下载整个图标集。所有图标都可以在个人或…...
day51 python CBAM注意力
目录 一、CBAM 模块简介 二、CBAM 模块的实现 (一)通道注意力模块 (二)空间注意力模块 (三)CBAM 模块的组合 三、CBAM 模块的特性 四、CBAM 模块在 CNN 中的应用 一、CBAM 模块简介 在之前的探索中…...

[C++错误经验]case语句跳过变量初始化
标题:[C错误经验]case语句跳过变量初始化 水墨不写bug 文章目录 一、错误信息复现二、错误分析三、解决方法 一、错误信息复现 write.cc:80:14: error: jump to case label80 | case 2:| ^ write.cc:76:20: note: crosses initialization…...
调试快捷键 pycharm vscode
目录 调试快捷键 pycharm vscode 修改快捷键 方法 1:通过菜单打开 方法 2:用快捷键打开 调试快捷键 pycharm Resume Program F9 Step Over F8 两个离的比较近,比较方便,比vscode的好。 vscode Continue F5 改为F9 S…...

成工fpga(知识星球号)——精品来袭
(如需要相关的工程文件请关注知识星球:成工fpga,https://t.zsxq.com/DMeqH,关注即送200GB学习资料,链接已置顶!) 《孩子都能学会的FPGA》系列是成工完成的第一个系列,也有一年多的时…...