【React Hooks原理 - useState】

概述

useState赋予了Function Component状态管理的能力,可以让你在不编写 class 的情况下使用 state 。其本质上就是一类特殊的函数,它们约定以 use 开头。本文从源码出发,一步一步看看useState是如何实现以及工作的。

基础使用

function useState<S>(initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] 

在组件顶层通过useState申明状态,useState接收一个参数作为该状态的初始值,该参数可以是任意类型的值,也可以是返回初始值的函数,React内部会进行判断然后执行并缓存该结果。返回一个包含当前状态和更新状态回调的数组

const [state, setState] = useState(initialState)

所有的Hooks都必须在组件顶层或者自定义Hook顶层中使用,不能在条件、循环语句中使用,这是为了避免Hook的位置混乱导致React无法正确找到状态和Hooks的对应而发生错误

源码解析

前面的文章我们提到过,我们使用的Hooks在React18之后在React内部拆分mount、update两个函数,并由dispatcher管理。下面我们将从入口、mount、update这三个方面来介绍useState的源码实现。

入口:文件路径在react/packages/react/src /ReactHooks.js

export function useState<S>(initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {const dispatcher = resolveDispatcher();return dispatcher.useState(initialState);
}

我们在组件内调用useState申明状态,实际就是执行的这个函数,然后通过dispatcher根据渲染阶段分发执行那个函数。

Mount挂载时

在挂载时,主要调用了mountStatemountStateImplmountWorkInProgressHook下面一一讲解。

mountState: 挂载时调用的函数,接收组件内传入的初始值initialState,然后返回当前状态和更新状态的回调

  • 调用mountStateImpl在当前渲染fiber节点中创建一个hook list用于对所有hook进行管理
  • 根据当前渲染fiber和更新队列创建一个dispatch用于更新状态,由于将dispatch绑定到dispatchSetState上,所以当我们通过set函数更新时,实际执行的是dispatchSetState函数
  • 将dispatch挂载到queue更新队列中,便于后续更新时候直接调用dispatch更新
  • 返回包含当前状态和更新回调的数组
function mountState<S>(initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {const hook = mountStateImpl(initialState);const queue = hook.queue;// 将dispatch绑定到dispatchSetState,所以当我们通过set函数更新时,实际执行的是dispatchSetState函数const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(null,currentlyRenderingFiber,queue,): any);queue.dispatch = dispatch;return [hook.memoizedState, dispatch];
}

hook和queue的数据结构如下:

hook = {memoizedState: initialValue, // 保存当前的状态值baseState: initialValue,     // 初始状态,用于批处理baseQueue: null,				// 需要更新的队列queue: null,                 // 更新队列,用于存储状态更新next: null                   // 链表中的下一个 Hook 节点
};const queue = {pending: null, // 指向尚未处理的更新。这些更新将在下一次渲染时被处理。lanes: NoLanes, // 优先级dispatch: null, // 用于分发动作(actions)触发状态更新。lastRenderedReducer: basicStateReducer, // 最后一次渲染时使用的 reducer 函数。对于 useReducer,它是用户定义的 reducer 函数;对于 useState,它是一个内置的基本状态 reducerlastRenderedState: (initialState: any), // 保存了上一次渲染时 Hook 的状态,以便在下一次渲染时能够对比新旧状态并进行必要的更新。};

在mountStateImpl函数中:

  • 调用mountWorkInProgressHook生成hook
  • 缓存初始值(如果是函数则执行并缓存结果),其中memoizedState会在update时计算新值,而baseState则记录初始值在批处理的时候会使用
  • 基于生成的hook初始化更新队列queue
  • 返回这个hook链表给到mountState继续处理
function mountStateImpl<S>(initialState: (() => S) | S): Hook {const hook = mountWorkInProgressHook();if (typeof initialState === 'function') {const initialStateInitializer = initialState;// $FlowFixMe[incompatible-use]: Flow doesn't like mixed typesinitialState = initialStateInitializer();}hook.memoizedState = hook.baseState = initialState;const queue: UpdateQueue<S, BasicStateAction<S>> = {pending: null,lanes: NoLanes,dispatch: null,lastRenderedReducer: basicStateReducer,lastRenderedState: (initialState: any),};hook.queue = queue;return hook;
}

mountWorkInProgressHook主要就是创建初始化一个hook链表,并将其挂载到当前渲染的fiber节点中。

function mountWorkInProgressHook(): Hook {const hook: Hook = {memoizedState: null,baseState: null,baseQueue: null,queue: null,next: null,};if (workInProgressHook === null) {// This is the first hook in the listcurrentlyRenderingFiber.memoizedState = workInProgressHook = hook;} else {// Append to the end of the listworkInProgressHook = workInProgressHook.next = hook;}return workInProgressHook;
}

至此useState在挂载时的整个流程就完成了,我们在这里小结一下。当我们在组件中使用useState时,其背后流程就是: Function Component -> FIber节点 -> Hooks链表 -> UpdateQueue -> dispatch(updateState)。每个函数组件都有一个对应的 Fiber 节点,每个 Fiber 节点都有一个 Hook 链表(比如保存组件中的useState、useEffect等所有hook),用于存储该组件中的所有 Hook。而Hook链表中的每个Hook都有一个UpdateQueue更新队列来对状态进行更新,在更新时会依次遍历这个Hooks链表然后执行对应Hook的更新。

fiber、hook、queue关系图:
在这里插入图片描述

Update更新时

上面我们介绍了在使用useState初始挂载一个状态时做了什么工作,现在来看看当组件重新渲染时useState是怎么实现的。

function updateState<S>(initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {return updateReducer(basicStateReducer, initialState);
}

从源码来看,如果使用过redux的同学可能会很熟悉,就和我们理解的一致也是通过dispatch来调用Reducer来进行updateState的。由此能看出useState 是基于 useReducer 实现,通过调用updateReducer来实现state更新,其中basicStateReducer 是React内部默认的处理状态更新的reducer。

function updateReducer<S, I, A>(reducer: (S, A) => S,initialArg: I,init?: (I) => S
): [S, Dispatch<A>] {const hook = updateWorkInProgressHook();return updateReducerImpl(hook, ((currentHook: any): Hook), reducer);
}

通过调用updateReducer将用于状态更新的reducer和初始值以及通过updateWorkInProgressHook处理后的链表传递给了updateReducerImpl进行处理。由于updateReducerImpl中代码比较多而且还涉及到了Scheduler调度中的优先级,所以对于部分跳过的更新逻辑在这里进行了省略,有兴趣的可以去官网查看【React Github】

updateWorkInProgressHook代码如下:该函数主要逻辑就是复用已有的hook并更新workInProgressHook指针指向下一个hook。优先复用当前渲染中的hook即workInProgress树中当前fiber的hook,如果没有则克隆页面显示的current fiber中的hook,如果都没有则通过throw抛出异常

updateWorkInProgressHook函数处理并更新指针到下一个hook是因为当前hook在上一次渲染或挂载过程中已经执行并存储了状态。这是因为React需要保持对hook链表的引用,以便在下一次渲染时可以复用这些hook。删除hook可能会导致状态丢失和链表结构破坏。所以在React的实现中,不会在上一次渲染时删除hook,而是在下一次渲染时更新指针。

function updateWorkInProgressHook(): Hook {
let nextCurrentHook: null | Hook; // 下次更新的hook
// nextCurrentHook值为当前渲染中fiber的下一次hook或者复用页面显示的当前fiber的下一个hook
if (currentHook === null) {// 通过alternate指针切换workInProgress和current fiber树const current = currentlyRenderingFiber.alternate;if (current !== null) {nextCurrentHook = current.memoizedState;} else {nextCurrentHook = null;}
} else {nextCurrentHook = currentHook.next;
}let nextWorkInProgressHook: null | Hook;
if (workInProgressHook === null) {nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
} else {nextWorkInProgressHook = workInProgressHook.next;
}if (nextWorkInProgressHook !== null) {// There's already a work-in-progress. Reuse it.workInProgressHook = nextWorkInProgressHook;nextWorkInProgressHook = workInProgressHook.next;currentHook = nextCurrentHook;
} else {// Clone from the current hook.if (nextCurrentHook === null) {const currentFiber = currentlyRenderingFiber.alternate;if (currentFiber === null) {// This is the initial render. This branch is reached when the component// suspends, resumes, then renders an additional hook.// Should never be reached because we should switch to the mount dispatcher first.throw new Error('Update hook called on initial render. This is likely a bug in React. Please file an issue.',);} else {// This is an update. We should always have a current hook.throw new Error('Rendered more hooks than during the previous render.');}}currentHook = nextCurrentHook;const newHook: Hook = {memoizedState: currentHook.memoizedState,baseState: currentHook.baseState,baseQueue: currentHook.baseQueue,queue: currentHook.queue,next: null,};if (workInProgressHook === null) {// This is the first hook in the list.currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;} else {// Append to the end of the list.workInProgressHook = workInProgressHook.next = newHook;}
}
return workInProgressHook;
}

updateReducerImpl代码如下:

/**** hook:指向当前 Fiber 节点正在处理的具体 Hook 实例(即 Hook 链表中的一个节点)。* current:指向当前 Fiber 节点中对应的 Hook 实例的当前状态(即已渲染到页面上的状态)。* reducer触发active进行state的更新*/
function updateReducerImpl<S, A>(hook: Hook,current: Hook,reducer: (S, A) => S
): [S, Dispatch<A>] {// 获取当前指向hook的更新队列,以及绑定reducer更新函数const queue = hook.queue;queue.lastRenderedReducer = reducer;let baseQueue = hook.baseQueue;// 如果有上次渲染未处理的更新队列const pendingQueue = queue.pending;if (pendingQueue !== null) {// 有上次为处理的更新以及本次也有需要处理的更新,则将两个更新队列合并,否则将上次未处理的赋值给更新队列等待本次渲染更新if (baseQueue !== null) {const baseFirst = baseQueue.next;const pendingFirst = pendingQueue.next;baseQueue.next = pendingFirst;pendingQueue.next = baseFirst;}current.baseQueue = baseQueue = pendingQueue;queue.pending = null;}// 如果本次没有更新队列,则更新memoizedState为baseStateconst baseState = hook.baseState;if (baseQueue === null) {hook.memoizedState = baseState;} else {// 更新队列有状态需要更新const first = baseQueue.next;let newState = baseState;let newBaseState = null;let newBaseQueueFirst = null;let newBaseQueueLast: Update<S, A> | null = null;let update = first;let didReadFromEntangledAsyncAction = false;do {const updateLane = removeLanes(update.lane, OffscreenLane);const isHiddenUpdate = updateLane !== update.lane;const shouldSkipUpdate = isHiddenUpdate? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane): !isSubsetOfLanes(renderLanes, updateLane);// 根据优先级判断当前是否需要跳过更新,并保存在newBaseQueueLast中在下次渲染时更新,然后调用markSkippedUpdateLanes跳过本次更新if (shouldSkipUpdate) {...} else {const revertLane = update.revertLane;// 根据优先级判断当前是否需要跳过更新,并保存在newBaseQueueLast中在下次渲染时更新if (!enableAsyncActions || revertLane === NoLane) {...} else {// 将符合本次更新条件的状态保存在update链表中,等待更新if (isSubsetOfLanes(renderLanes, revertLane)) {update = update.next;if (revertLane === peekEntangledActionLane()) {didReadFromEntangledAsyncAction = true;}continue;} else {// 不符合的保存在newBaseQueueLast等待下次渲染时候更新...}}// 开始更新,当前update对象是否提前计算,否则通过reducer处理const action = update.action;if (update.hasEagerState) {// If this update is a state update (not a reducer) and was processed eagerly,// we can use the eagerly computed statenewState = ((update.eagerState: any): S);} else {newState = reducer(newState, action);}}update = update.next;} while (update !== null && update !== first);// 遍历本次更新队列之后,判断是否有跳过的更新,如果有则保存在newBaseState中,等待下次渲染时更新if (newBaseQueueLast === null) {newBaseState = newState;} else {newBaseQueueLast.next = (newBaseQueueFirst: any);}// 判断上一次的状态和reducer更新之后的状态是否一致,发生变化则通过markWorkInProgressReceivedUpdate函数给当前fiber打上update标签if (!is(newState, hook.memoizedState)) {markWorkInProgressReceivedUpdate();// 检查异步处理操作,如果说是异步获取值,则需要通过throw entangledActionThenable将当前更新挂起if (didReadFromEntangledAsyncAction) {const entangledActionThenable = peekEntangledActionThenable();if (entangledActionThenable !== null) {throw entangledActionThenable;}}}// 将本次新的state保存在memoizedState中hook.memoizedState = newState;// 保存下次更新的初始值,如果本次没有跳过更新,该值为更新后通过reducer或者eagerState计算的新值,有跳过的更新则会本次更新前原来的初始值hook.baseState = newBaseState;// 将本次跳过的更新保存在baseQueue更新队列中中,下次渲染时更新hook.baseQueue = newBaseQueueLast;queue.lastRenderedState = newState;}// 没有状态更新时,将当前队列优先级设置为默认if (baseQueue === null) {queue.lanes = NoLanes;}const dispatch: Dispatch<A> = (queue.dispatch: any);return [hook.memoizedState, dispatch];
}

根据上面代码以及注释可知,在updateReducerImpl主要做了一下功能:

  • 获取和合并更新队列。
  • 遍历更新队列,根据优先级判断是否需要跳过,计算新的更新队列
  • 通过reducer进行状态更新
  • 更新基础状态和队列
  • 处理异步操作的影响
  • 返回新的状态和 dispatch 函数

useState于update的差异

根据下面表格可以简要总结两者差异主要是:mountState进行初始化挂载,updateState在其基础上进行更新队列处理,包括跳过低优先级任务以及提前处理任务。

比较点mountStateupdateState
创建 Hook 和更新队列
返回值返回包含当前状态值和 set 函数的数组返回包含当前状态值和 set 函数的数组
用途主要用于组件的首次渲染,初始化数据挂载处理组件的更新渲染,包含对上次渲染跳过的部分进行处理
初始状态处理计算并设置初始状态(如果 initialState 是函数,会调用它)不处理初始状态,只处理更新
更新队列创建新的更新队列,并将其分配给 Hook处理现有更新队列,可能需要合并新的和已有的更新队列
状态计算直接设置初始状态通过调用 reducer(通常是 basicStateReducer)计算新状态
Hook 链表将 Hook 添加到当前 Fiber 的 Hook 链表中更新当前 Fiber 的 Hook 链表中的现有 Hook
优先级处理不涉及优先级处理可能需要处理优先级和跳过的更新

set函数

由上面知道了,使用useSatte会返回包含[newState,setValue]的数组,然后我们调用setValue可以更新状态值,而这个dispatch实际是执行的dispatchSetState(在mountState中通过bind进行了绑定),所以下面来看看在dispatchSetState函数中做了什么?

// 触发更新后,React会判断当前是否还有其他渲染或者挂起,没有会提前计算值,不变则跳过更新
function dispatchSetState<S, A>(fiber: Fiber,queue: UpdateQueue<S, A>,action: A,): void {// 获取优先级const lane = requestUpdateLane(fiber);// 创建一个update更新对象const update: Update<S, A> = {lane,revertLane: NoLane,action,hasEagerState: false,eagerState: null,next: (null: any),};// 当前是否是渲染阶段if (isRenderPhaseUpdate(fiber)) {// 申请加入正在渲染的更新队列enqueueRenderPhaseUpdate(queue, update);} else {// 页面上显示的fiber树 - 双缓冲树,通过alternate切换指针const alternate = fiber.alternate;// 判断current树和workInProgress树是否有挂起的更新,如果没有则进入提前计算逻辑if (fiber.lanes === NoLanes &&(alternate === null || alternate.lanes === NoLanes)) {const lastRenderedReducer = queue.lastRenderedReducer;if (lastRenderedReducer !== null) {let prevDispatcher = null;try {const currentState: S = (queue.lastRenderedState: any);// 用于update state的reducer,由React内部提供,所以这里使用缓存的lastRenderedReducer可以提高性能const eagerState = lastRenderedReducer(currentState, action);// 标识该值已经提前计算update.hasEagerState = true;update.eagerState = eagerState;if (is(eagerState, currentState)) {// 无更新则跳过更新步骤enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);return;}} catch (error) {// Suppress the error. It will throw again in the render phase.} finally {}}}// 进入组件更新步骤 enqueueConcurrentHookUpdate会将update更新对象添加到enqueueUpdate更新队列中const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);if (root !== null) {// 进入Scheduler调度阶段,进行fiber构造scheduleUpdateOnFiber(root, fiber, lane);entangleTransitionUpdate(root, queue, lane);}}
}

从代码中能知道在函数中主要:

  • 创建一个带有优先级的update对象
  • 判断当前是否是渲染更新阶段,如果是则直接将update对象添加到更新队列中等待更新,否则判断current树和workInProgress树是否有挂起的更新,如果没有则进入提前计算并判断值是否变化,没有变化则跳过更新,否则就进入组件更新步骤将update对象添加到enqueueUpdats更新队列中然后调用scheduleUpdateOnFiber等待调度更新

其中enqueueConcurrentHookUpdate函数主要就是将创建的update对象添加到当前fiber的更新队列enqueueUpdate中,其中通过MapSet来对渲染更新数据进行管理

function enqueueRenderPhaseUpdate<S, A>(queue: UpdateQueue<S, A>,update: Update<S, A>,) {if (renderPhaseUpdates === null) {renderPhaseUpdates = new Map();}let firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);if (firstRenderPhaseUpdate === undefined) {renderPhaseUpdates.set(queue, update);} else {// Append the update to the end of the list.let lastRenderPhaseUpdate = firstRenderPhaseUpdate;while (lastRenderPhaseUpdate.next !== null) {lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;}lastRenderPhaseUpdate.next = update;}}

将本次更新的update对象添加到更新队列中,会调用scheduleUpdateOnFiber等待调度更新然后进行新的fiber树构造。

这里从宏观角度,简单说下React多个包之间的流程来帮助我们理解。 React中有几个核心包:react、react-dom、react-reconciler(协调)、scheduler(调度),其中上面的update更新对象处理以及将其添加到更新队列中是在react包中处理的,然后调用scheduler包中的scheduleUpdateOnFiber等待调度进入react-reconciler包中处理进行renderer阶段进行fiber构造,最后进入react-dom进行commit阶段进行页面的渲染。
在这里插入图片描述
上图就是React总的流程图,各个核心流程都在里面,有兴趣的同学可以查看我写的其他React源码系列,比如【React架构 - Fiber构造循环】

总结

上面说了这么多这里进行简单总结一下,有的点可能会多次提及为了巩固记忆。

正文开始~~

在页面渲染过程中有两个阶段分别为mount(首次渲染)、update(更新渲染),而React为了更好的管理和优化副作用将Hooks(useContext除外)拆为了mount、update两个函数。通过内置的dispatcher管理,React会根据目前具体处于什么阶段来决定调用那个函数,比如在mount阶段,会调用mountState函数,这对于开发者来说是无感的,React在内部进行了映射。具体的流程走向如下图所示

mount、update时,state的处理:
在这里插入图片描述

当触发set函数进行状态更新时:

在这里插入图片描述
以上都是根据自己理解进行总结梳理的,如果理解有误还请评论指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/374142.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt/QML学习-定位器

QML学习 定位器例程视频讲解代码 main.qml import QtQuick 2.15 import QtQuick.Window 2.15Window {width: 640height: 480visible: truetitle: qsTr("positioner")Rectangle {id: rectColumnwidth: parent.width / 2height: parent.height / 2border.width: 1Col…

使用PEFT库进行ChatGLM3-6B模型的QLORA高效微调

PEFT库进行ChatGLM3-6B模型QLORA高效微调 QLORA微调ChatGLM3-6B模型安装相关库使用ChatGLM3-6B模型GPU显存占用准备数据集加载数据集数据处理数据集处理加载量化模型-4bit预处理量化模型配置LoRA适配器训练超参数配置开始训练保存LoRA模型模型推理合并模型使用微调后的模型 QLO…

禁止使用存储过程

优质博文&#xff1a;IT-BLOG-CN 灵感来源 什么是存储过程 存储过程Stored Procedure是指为了完成特定功能的SQL语句集&#xff0c;经编译后存储在数据库中&#xff0c;用户可通过指定存储过程的名字并给定参数&#xff08;如果该存储过程带有参数&#xff09;来调用执行。 …

前端vue 实现取色板 的选择

大概就是这样的 一般的web端框架 都有自带的 的 比如 ant-design t-design 等 前端框架 都是带有这个的 如果遇到没有的我们可以自己尝试开发一下 简单 的 肯定比不上人家的 但是能用 能看 说的过去 我直接上代码了 其实这个取色板 就是一个input type 是color 的input …

一文清晰了解CSS——简单实例

首先一个小技巧&#xff1a; 一定要学会的vsCode格式化整理代码的快捷键&#xff0c;再也不用手动调格式了-腾讯云开发者社区-腾讯云 (tencent.com) CSS选择器用于选择要应用样式的HTML元素。常见的选择器包括&#xff1a; 类选择器&#xff1a;以.开头&#xff0c;用于选择具…

Paimon下载使用和基础操作说明

简介 Apache Paimon 是一种湖格式&#xff0c;支持使用 Flink 和 Spark 构建实时湖仓一体架构 用于流式处理和批处理操作。Paimon创新性地将湖格式与LSM&#xff08;Log-structured merge-tree&#xff09;相结合 结构&#xff0c;将实时流式更新引入 Lake 架构。 Paimon提供以…

C语言 | Leetcode C语言题解之第228题汇总区间

题目&#xff1a; 题解&#xff1a; char** summaryRanges(int* nums, int numsSize, int* returnSize) {char** ret malloc(sizeof(char*) * numsSize);*returnSize 0;int i 0;while (i < numsSize) {int low i;i;while (i < numsSize && nums[i] nums[i …

科普文:jvm笔记

一、JVM概述# 1. JVM内部结构# 跨语言的平台&#xff0c;只要遵循编译出来的字节码的规范&#xff0c;都可以由JVM运行 虚拟机 系统虚拟机 VMvare 程序虚拟机 JVM JVM结构 HotSpot虚拟机 详细结构图 前端编译器是编译为字节码文件 执行引擎中的JIT Compiler编译器是把字节…

Pycharm python解释器 unsupported python 3.1 解决

Pycharm 环境 unsupported python 3.1解决 1. 问题重现2. 原因分析3. 解决方法 1. 问题重现 之前使用Pycharm 2024.1.1的时候&#xff0c;环境配置的Python 3.11.9&#xff0c;现在改成使用Pycharm 2020.2.2&#xff0c;结果Python解释器显示“unsupported python 3.1”&#…

PyTorch复现PointNet——模型训练+可视化测试显示

因为项目涉及到3D点云项目&#xff0c;故学习下PointNet这个用来处理点云的神经网络 论文的话&#xff0c;大致都看了下&#xff0c;网络结构有了一定的了解&#xff0c;本博文主要为了下载调试PointNet网络源码&#xff0c;训练和测试调通而已。 我是在Anaconda下创建一个新的…

排序相关算法--1.插入排序+冒泡排序回顾

1.基本分类 2.插入排序 特点&#xff1a;有实践意义&#xff08;例如后期快排的优化&#xff09;&#xff0c;适应性强&#xff0c;一般不会到时间复杂度最坏的情况。 将第一个元素视为已经排好序的序列。取出下一个元素&#xff0c;在已经排好序的序列中从后往前比较&#xf…

硅纪元AI应用推荐 | 百度橙篇成新宠,能写万字长文

“硅纪元AI应用推荐”栏目&#xff0c;为您精选最新、最实用的人工智能应用&#xff0c;无论您是AI发烧友还是新手&#xff0c;都能在这里找到提升生活和工作的利器。与我们一起探索AI的无限可能&#xff0c;开启智慧新时代&#xff01; 百度橙篇&#xff0c;作为百度公司在202…

算法通关:004_1选择排序

代码一定要自己手敲理解 public class _004 {//选择排序&#xff0c;冒泡排序&#xff0c;插入排序//交换public static void swap(int[] arr,int i ,int j){int temp arr[i];arr[i] arr[j];arr[j] temp;}//选择排序public static void selectSort(int[] arr){if(arr null…

0基础学会在亚马逊云科技AWS上搭建生成式AI云原生Serverless问答QA机器人(含代码和步骤)

小李哥今天带大家继续学习在国际主流云计算平台亚马逊云科技AWS上开发生成式AI软件应用方案。上一篇文章我们为大家介绍了&#xff0c;如何在亚马逊云科技上利用Amazon SageMaker搭建、部署和测试开源模型Llama 7B。下面我将会带大家探索如何搭建高扩展性、高可用的完全托管云原…

初次用bable遍历vue项目下的中文

利用 babel 找到 AST 中的中文 // vite-plugin-babel-transform.js const parser require(babel/parser) const traverse require(babel/traverse).default // const types require(babel/types) // const generate require(babel/generator).default const fs require(f…

nginx的正向代理和反向代理

nginx的正向代理和反向代理 正向代理以及缓存配置&#xff1a; 代理&#xff1a;客户端不再是直接访问服务端&#xff0c;通过代理服务器访问服务端 正向代理&#xff1a;面向客户端&#xff0c;我们通过代理服务器的IP地址访问目标服务端。 服务端只知道代理服务器的地址&am…

公司内部配置GitLab,通过SSH密钥来实现免密clone、push等操作

公司内部配置GitLab&#xff0c;通过SSH密钥来实现免密clone、push等操作。以下是配置SSH密钥以实现免密更新的步骤&#xff1a; 1.生成SSH密钥 在本地计算机上打开终端或命令提示符。输入以下命令以生成一个新的SSH密钥&#xff1a;ssh-keygen -t rsa -b 4096 -C "your…

《C++设计模式》状态模式

文章目录 一、前言二、实现一、UML类图二、实现 一、前言 状态模式理解最基本上的我觉得应该也是够用了&#xff0c;实际用的话&#xff0c;也应该用的是Boost.MSM状态机。 相关代码可以在这里&#xff0c;如有帮助给个star&#xff01;AidenYuanDev/design_patterns_in_mode…

2970.力扣每日一题7/10 Java(暴力枚举)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;算法练习关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 解题思路 解题方法 时间复杂度 空间复杂度 Code 解题思路 incre…

Elastic Stack--15--聚合查询(SUM、MAX、MIN、AVG)案例

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 ES的聚合查询(SUM、MAX、MIN、AVG)1.求和查询2.求平均值3.最大最小值查询4.唯一值查询 (类似于sql中的distinct 去重)5.stats聚合 ES的聚合查询(SUM、MAX、MIN、AVG…