React18 setState是同步还是异步?
相信大家对于react的setState肯定是不陌生了, 这是一个用于更新状态的函数. 但是在之前有一道非常经典的面试题就是关于setState是同步还是异步的问题, 具体可以参考我之前写的一篇文章: 一篇文章彻底理解setState是同步还是异步!. 对于react 18之前的版本, 上文说的东西确实没错, 但是react团队已经在18中对批处理的行为做了更改, 会尽可能的将所有能进行批处理的内容都进行批处理, 以获取更好的性能, 今天我们就来聊聊react 18中对这一行为做了哪些更改.
先看现象, 再说结论
我们都用下面这段代码在不同版本的react上进行测试
class App extends React.Component {state = {data: 1}test = () => {setTimeout(() => {this.setState({data: 2});console.log('data', this.state.data);this.setState({data: 3});console.log('data', this.state.data);}, 0);}render() {console.log("render");return (<div><button onClick={this.test}>{this.state.data}</button></div>)}
}
大家觉得输出的data值会是什么, 这个render又会打印几次?
在react 17.x下, 我们能够同步的获取到data的值, 所以输出会是2, 3.同时也会经历两次react的整个render过程, 所以也会导致render被打印两次, 这都是因为setTimeout带来的影响, 具体的解释可以参考之前的文章.

而在最新版的react 18上, 这两个setData也会被异步的批处理, 合并为一次进行更新, 所以我们拿到的值始终是1, render函数也只会被打印一次.

react 18做了什么修改
官方的说明在这里: https://github.com/reactwg/react-18/discussions/21
总结一下就是:
从react 18开始, 使用了createRoot创建应用后, 所有的更新都会自动进行批处理(也就是异步合并).使用render的应用会保持之前的行为.
如果你想保持同步更新行为, 可以使用ReactDOM.flushSync().
// 新版
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const container = document.getElementById('root');
// Create a root.
const root = ReactDOM.createRoot(container);
// Render the top component to the root.
root.render(<App />);// 旧版
// import React from 'react';
// import ReactDOM from 'react-dom';
// import './index.css';
// import App from './App';
// import reportWebVitals from './reportWebVitals';// ReactDOM.render(
// <React.StrictMode>
// <App />
// </React.StrictMode>,
// document.getElementById('root')
// );// // If you want to start measuring performance in your app, pass a function
// // to log results (for example: reportWebVitals(console.log))
// // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
// reportWebVitals();
至于为什么会这样, 我们直接从源码入手就好了.
我们知道, react的setState最终会走到scheduleUpdateOnFiber来进行更新, 之前最关键的一段代码就在这里
// react 17.x
if (executionContext === NoContext) {// Flush the synchronous work now, unless we're already working or inside// a batch. This is intentionally inside scheduleUpdateOnFiber instead of// scheduleCallbackForFiber to preserve the ability to schedule a callback// without immediately flushing it. We only do this for user-initiated// updates, to preserve historical behavior of legacy mode.resetRenderTimer();flushSyncCallbackQueue();
}
executionContext代表了react当前的调度状态, 如果退出了react的调度这个值就会重新变成NoContext. 也就是说, 如果你调用setState的时候并不处于react的调度状态中, 那么就会同步的去执行你的setState.这也是为什么一旦我们使用一些异步操作就会导致setState变成同步的原因, 而在react 18中这段代码变成了这样
// react 18.x
if (lane === SyncLane && executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.!( ReactCurrentActQueue$1.isBatchingLegacy)) {// Flush the synchronous work now, unless we're already working or inside// a batch. This is intentionally inside scheduleUpdateOnFiber instead of// scheduleCallbackForFiber to preserve the ability to schedule a callback// without immediately flushing it. We only do this for user-initiated// updates, to preserve historical behavior of legacy mode.resetRenderTimer();flushSyncCallbacksOnlyInLegacyMode();
}
可以看到我们多出了好几个判断条件, 除了之前的 executionContext === NoContext 之外, 还多了三个判断条件, 我们一个一个来看看.
lane === SyncLane
这个其实没什么好说的, lane是react中和优先级有关的概念, 从函数命名就可以看出, 只有是同步任务才会进到这个if.
var TotalLanes = 31;
var NoLanes =
/* */
0;
var NoLane =
/* */
0;
var SyncLane =
/* */
1;
var InputContinuousHydrationLane =
/* */
2;
var InputContinuousLane =
/* */
4;
var DefaultHydrationLane =
/* */
8;
var DefaultLane =
/* */
16;
var TransitionHydrationLane =
/* */
32;
var TransitionLanes =
/* */
4194240;
var TransitionLane1 =
/* */
64;
var TransitionLane2 =
/* */
128;
var TransitionLane3 =
/* */
256;
var TransitionLane4 =
/* */
512;
var TransitionLane5 =
/* */
1024;
var TransitionLane6 =
/* */
2048;
var TransitionLane7 =
/* */
4096;
var TransitionLane8 =
/* */
8192;
var TransitionLane9 =
/* */
16384;
var TransitionLane10 =
/* */
32768;
var TransitionLane11 =
/* */
65536;
var TransitionLane12 =
/* */
131072;
var TransitionLane13 =
/* */
262144;
var TransitionLane14 =
/* */
524288;
var TransitionLane15 =
/* */
1048576;
var TransitionLane16 =
/* */
2097152;
var RetryLanes =
/* */
130023424;
var RetryLane1 =
/* */
4194304;
var RetryLane2 =
/* */
8388608;
var RetryLane3 =
/* */
16777216;
var RetryLane4 =
/* */
33554432;
var RetryLane5 =
/* */
67108864;
var SomeRetryLane = RetryLane1;
var SelectiveHydrationLane =
/* */
134217728;
var NonIdleLanes =
/* */
268435455;
var IdleHydrationLane =
/* */
268435456;
var IdleLane =
/* */
536870912;
var OffscreenLane =
/* */
1073741824; // This function is used for the experimental timeline (react-devtools-timeline)
// It should be kept in sync with the Lanes values above.
这一堆东西就代表了react内部各种不同优先级的任务.
(fiber.mode & ConcurrentMode) === NoMode
react fiber的mode属性代表了不同的渲染模式
var NoMode =
/* */
0; // TODO: Remove ConcurrentMode by reading from the root tag insteadvar ConcurrentMode =
/* */
1;
var ProfileMode =
/* */
2;
var DebugTracingMode =
/* */
4;
var StrictLegacyMode =
/* */
8;
var StrictEffectsMode =
/* */
16;
具体是什么意思呢, 比如说ProfileMode代表了性能调试模式, 我们的开发环境的mode就会被赋予这个值, 可以在控制台中给开发者输出一些提示信息.而其他的值是啥意思呢..其实我目前也不能很明白的给大家说清楚, 因为我也没研究过, 但是只要知道这是个区分不同模式的变量就行了.
而这个值会在什么地方赋给fiber的mode属性呢, 会在我们整个应用的入口, 然后经过一系列的函数调用, 最终会走到createHostRootFiber方法
function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) {var mode;if (tag === ConcurrentRoot) {mode = ConcurrentMode;if (isStrictMode === true) {mode |= StrictLegacyMode;{mode |= StrictEffectsMode;}}} else {mode = NoMode;}if ( isDevToolsPresent) {// Always collect profile timings when DevTools are present.// This enables DevTools to start capturing timing at any point–// Without some nodes in the tree having empty base times.mode |= ProfileMode;}return createFiber(HostRoot, null, null, mode);
}
其中tag的值就是根据入口函数一路传下来的. 我们之前提到, 要想体验自动批处理需要在应用入口将ReactDOM.render替换为ReactDOM.createRoot, 这两者有什么区别呢:
function render(element, container, callback) {// 省略...return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
}function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {// 省略...if (!root) {// Initial mountroot = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);fiberRoot = root;// 省略...} else {// 省略...}return getPublicRootInstance(fiberRoot);
}function legacyCreateRootFromDOMContainer(container, forceHydrate) {// 省略...var root = createContainer(container, LegacyRoot, forceHydrate, null, // hydrationCallbacksfalse, // isStrictModefalse, // concurrentUpdatesByDefaultOverride,'' // identiferPrefix);// 省略...return root;
}function createContainer(containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix) {return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix);
}function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix) {// 省略...var uninitializedFiber = createHostRootFiber(tag, isStrictMode);root.current = uninitializedFiber;uninitializedFiber.stateNode = root;// 省略...return root;
}function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) {var mode;if (tag === ConcurrentRoot) {mode = ConcurrentMode;if (isStrictMode === true) {mode |= StrictLegacyMode;{mode |= StrictEffectsMode;}}} else {mode = NoMode;}if ( isDevToolsPresent) {// Always collect profile timings when DevTools are present.// This enables DevTools to start capturing timing at any point–// Without some nodes in the tree having empty base times.mode |= ProfileMode;}return createFiber(HostRoot, null, null, mode);
}
我这里把从ReactDOM.render到createHostRootFiber的全部函数都放在了这里, 可以看到最终createHostRootFiber拿到的tag值其实是LegacyRoot, 所以mode最终会等于NoMode. 随后如果在开发环境下还会被赋予一个ProfileMode, 也就是我们之前说的调试模式.
所以即使我们升级到了18.x的版本, 但是如果仍然使用ReactDOM.render来创建我们的应用, 我们就会在这个条件得到false
(fiber.mode & ConcurrentMode) === NoMode
因为我们的fiber.mode并没有被赋值为ConcurrentMode.而当我们使用ReactDOM.createRoot来创建应用时, 也是一路跟着函数找下去, 会发现tag最后拿到的是ConcurrentRoot, 也就是说mode会被赋值为ConcurrentMode, 所以也就是为什么只有使用了ReactDOM.createRoot的应用才会有该特性的原因.
ps: mode是可以被赋值为多个值的, 区分这多个值是通过&操作和|操作, 因为mode其实是一个二进制值, 不理解这块的同学可以再想想二进制值的&、|操作.
!( ReactCurrentActQueue$1.isBatchingLegacy)
其实这个条件注释已经写的很清楚了
// Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
就是在act函数调用的时候也进行同步更新, act是个啥玩意呢.我们直接抄一下官网的介绍:
在编写UI测试时,可以将渲染、用户事件或数据获取等任务视为与用户界面交互的“单元”。react-dom/test-utils提供了一个名为act()的 helper, 它确保在进行任何断言之前, 与这些“单元”相关的所有更新都已处理并应用于DOM:
act(() => {// 渲染组件
});
// 进行断言
这有助于使测试运行更接近真实用户在使用应用程序时的体验。这些示例的其余部分使用act()来作出这些保证。
而isBatchingLegacy也正是在act函数中被赋值为true
function act(callback) {{// `act` calls can be nested, so we track the depth. This represents the// number of `act` scopes on the stack.var prevActScopeDepth = actScopeDepth;actScopeDepth++;if (ReactCurrentActQueue.current === null) {// This is the outermost `act` scope. Initialize the queue. The reconciler// will detect the queue and use it instead of Scheduler.ReactCurrentActQueue.current = [];}var prevIsBatchingLegacy = ReactCurrentActQueue.isBatchingLegacy;var result;try {// Used to reproduce behavior of `batchedUpdates` in legacy mode. Only// set to `true` while the given callback is executed, not for updates// triggered during an async event, because this is how the legacy// implementation of `act` behaved.ReactCurrentActQueue.isBatchingLegacy = true;result = callback(); // Replicate behavior of original `act` implementation in legacy mode,// which flushed updates immediately after the scope function exits, even// if it's an async function.if (!prevIsBatchingLegacy && ReactCurrentActQueue.didScheduleLegacyUpdate) {var queue = ReactCurrentActQueue.current;if (queue !== null) {ReactCurrentActQueue.didScheduleLegacyUpdate = false;flushActQueue(queue);}}} catch (error) {popActScope(prevActScopeDepth);throw error;} finally {ReactCurrentActQueue.isBatchingLegacy = prevIsBatchingLegacy;}//...省略}
}
综上所述
所以, 在升级到18版本之后的react只有在你使用ReactDOM.render的时候(LegacyMode)才会保持之前的行为, 否则都会对你的更新进行合并处理, 也就是自动批处理. 从我们最后调用的函数名也能看出这一点: flushSyncCallbacksOnlyInLegacyMode
被遗忘的 flushSync
我们之前还提到, 如果我们想进行同步更新可以使用flushSync函数, 那么它又干了啥.
function flushSync(fn) {// In legacy mode, we flush pending passive effects at the beginning of the// next event, not at the end of the previous one.if (rootWithPendingPassiveEffects !== null && rootWithPendingPassiveEffects.tag === LegacyRoot && (executionContext & (RenderContext | CommitContext)) === NoContext) {flushPassiveEffects();}var prevExecutionContext = executionContext;executionContext |= BatchedContext;var prevTransition = ReactCurrentBatchConfig$3.transition;var previousPriority = getCurrentUpdatePriority();try {ReactCurrentBatchConfig$3.transition = 0;setCurrentUpdatePriority(DiscreteEventPriority);if (fn) {return fn();} else {return undefined;}} finally {setCurrentUpdatePriority(previousPriority);ReactCurrentBatchConfig$3.transition = prevTransition;executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.// Note that this will happen even if batchedUpdates is higher up// the stack.if ((executionContext & (RenderContext | CommitContext)) === NoContext) {flushSyncCallbacks();}}
}
可以看到, 这个函数会在执行完传给他的fn函数后马上去清空一次更新队列, 也就是调用flushSyncCallbacks方法, 就是我们之前在异步中调用setState的行为.
值得一提的是, 如果我们在react 18中想达到之前的效果, 这样写是不行的:
import React from 'react';
import { flushSync } from 'react-dom';class App extends React.Component {state = {data: 1}test = () => {setTimeout(() => {flushSync(() => {this.setState({data: 2});console.log('data', this.state.data);this.setState({data: 3});console.log('data', this.state.data);});}, 0);}render() {console.log("render");return (<div><button onClick={this.test}>{this.state.data}</button></div>)}
}export default App;
这样写两个setState还是会被合并为同一个, 因为调用完setState之后并不会马上去刷新更新队列, 只有在整个函数执行完以后才会对队列进行刷新. 所以如果两个setState都写在一个flushSync里面是没有效果的.要想达到之前的效果需要这样写:
import React from 'react';
import { flushSync } from 'react-dom';class App extends React.Component {state = {data: 1}test = () => {setTimeout(() => {flushSync(() => {this.setState({data: 2});});console.log('data', this.state.data);flushSync(() => {this.setState({data: 3});});console.log('data', this.state.data);}, 0);}render() {console.log("render");return (<div><button onClick={this.test}>{this.state.data}</button></div>)}
}
ps: 我们一直说的同步异步并不是指setState本身, setState本身一直一个同步函数, 我们指的是调用完setState后react会同步的去执行后续的步骤还是会异步的去执行后续的步骤.
结语
react官方做出这个改变其实也是为了更好的性能去考虑的, 毕竟调用完setState之后同步的进行渲染有时候会导致很多没必要的开销, 特别是在进行数据请求时, 很容易写出多个同步的setState.
而随着这波更新, 可能以后这道经典的面试题也会随之消散, 毕竟往后不论在什么情况下, 默认行为都会帮你对setState进行合并更新, 不再会进行同步处理了.
相关文章:

React18 setState是同步还是异步?
相信大家对于react的setState肯定是不陌生了, 这是一个用于更新状态的函数. 但是在之前有一道非常经典的面试题就是关于setState是同步还是异步的问题, 具体可以参考我之前写的一篇文章: 一篇文章彻底理解setState是同步还是异步!. 对于react 18之前的版本, 上文说的…...
Kafka消费者 TCP管理
Kafka消费者 TCP管理创建 TCPFindCoordinator连接协调者消费数据TCP 连接数关闭 TCP 连接消费者的程序入口类是 KafkaConsumer 构建 KafkaConsumer 时 ,不会创建任何 TCP 连接TCP 连接是用 KafkaConsumer.poll 创建 创建 TCP poll 创建 TCP 的地方 : 发起 FindC…...

软考高级备考哪一个类型好些?
软考高级是比中级和初级难,科目就要考三科,选择题基础知识简答题案例分析写作论文 软考高级科目有:信息系统项目管理师、系统分析师、系统架构设计师、网络规划师、系统规划与管理师。如下: 软考高级中高项信息系统项目管理师师比…...
2023 HBU 天梯赛第一次测试 题目集
目录 1 建校日期 2 发射小球 3 背上书包去旅行 4 吉利的数字 5 向前走 6 热水器 7 走方格 8 朋友圈 9 交保护费 10 走方格 11 和与积 12 缩短字符串 13 买木棒 1 建校日期 在2022 ICPC沈阳站上,东北大学命题组给参赛的选手们出了一道签到题࿰…...

华为OD机试题,用 Java 解【子序列长度】问题
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…...

内网环境解决SSL证书问题
本来这个没什么好写的,但是坑实在有点多,不得不写个文章记录下来。 创建证书看这里!!! 很多知识点要结合这个页面内容来看。 创建证书已经看过相关文章,然后用unity跑的时候发现连不上,完全没…...
数据分析方法01对比分析法
对比分析法 1、概念 基于相同的数据标准下,把两个及以上相互联系的指标数据进行比较,准确量化的分析他们的差异,说明研究对象在规模大小,水平高低,速度快慢等的不同表现,目的是为了找到差异的原因&#x…...

基于SMOKE多模式排放清单处理技术及EDGAR/MEIC清单制作与VOCs排放量核算
查看原文>>>基于SMOKE多模式排放清单处理技术及EDGAR/MEIC清单制作与VOCs排放量核算 (qq.com)随着我国经济快速发展,我国面临着日益严重的大气污染问题。近年来,严重的大气污染问题已经明显影响国计民生,引起政府、学界和人们越来越…...

CSS流动布局-页面自适应
项目中经常会碰到页面自适应的问题,例如:商城的列表展示、分类列表展示等页面,如下: 该页面会随着页面的放大缩小而随之发生变化,这种自适应的页面布局在大屏幕、小屏幕、不同的浏览器设备上都应该呈现出与设计匹配的…...

3.Elasticsearch初步进阶
3.Elasticsearch初步进阶[toc]1.文档批量操作批量获取文档数据批量获取文档数据是通过_mget的API来实现的在URL中不指定index和type请求方式:GET请求地址:_mget功能说明:可以通过ID批量获取不同index和type的数据请求参数docs:文档数组参数_index:指定index_type:指定type_id:指…...

优思学院|六西格玛管理的核心理念是什么?
六西格玛管理是一种基于数据分析的质量管理方法,旨在通过降低过程的变异性来达到质量稳定和优化的目的。该方法以希腊字母“σ”为名,代表标准差,是衡量过程变异性的重要指标。 六西格玛管理的核心理念是“以客户为中心、以数据为基础、追求…...

第十七节 多态
多态 什么是多态? ●同类型的对象,执行同一个行为,会表现出不同的行为特征。 多态的常见形式 父类类型 对象名称new子类构造器; 接口 对象名称new 实现类构造器; 多态中成员访问特点 ●方法调用:编译看左边,运行看右边。 ●变量调用:编译看…...

[vue]提供一种网站底部备案号样式代码
演示 vue组件型(可直接用) 组件代码:copyright-icp.vue <template><div class"icp">{{© ${year} ${author} }}<a href"http://beian.miit.gov.cn/" target"_blank">{{ record }}</a…...

python第四天作业~函数练习
目录 作业4、判断以下哪些不能作为标识符 A、a B、¥a C、_12 D、$a12 E、false F、False 作业5: 输入数,判断这个数是否是质数(要求使用函数 for循环) 作业6:求50~150之间的质数是…...

linux安装influxdb-rpmyum方式
一、influxdb的安装InfluxDB简介时序数据库InfluxDB版是一款专门处理高写入和查询负载的时序数据库,用于存储大规模的时序数据并进行实时分析,包括来自DevOps监控、应用指标和IoT传感器上的数据主要特点:专为时间序列数据量身订造高性能数据存…...
死锁
1.死锁的定义 多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法…...

C++基础了解-05-C++常量
C常量 一、C常量 常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。 常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值。 常量就像是常规的变量,只不过常量的值在定义后不能进…...
深度学习笔记-2.自动梯度问题
通过反向传播进行自动求梯度1-requires_grad问题2-梯度3- detach() 和 with torch.no_grad()4- Tensor.data.requires_gradPyTorch提供的autograd包能够根据输入和前向传播过程自动构建计算图,并执行反向传播. 1-requires_grad问题 requires_gradTrue …...

一文读懂倒排序索引涉及的核心概念
基础概念相信对于第一次接触Elasticsearch的同学来说,最难理解的概念就是倒排序索引(也叫反向索引),因为这个概念跟我们之前在传统关系型数据库中的索引概念是完全不同的!在这里我就重点给大家介绍一下倒排序索引&…...

Java基础算法题
以创作之名致敬节日 胜固欣然,败亦可喜。 --苏轼 目录 练习1 : 优化代码 扩展 : CRTL Alt M 自动抽取方法 练习2: 方法一: 方法二: 方法三: Math : 顾名思义,Math类就是用来进行数学计算的,它提供了大量的静态方法来便于我们实…...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...