React16源码: React中的completeUnitOfWork的源码实现
completeUnitOfWork
1 )概述
- 各种不同类型组件的一个更新过程对应的是在执行
performUnitOfWork
里面的beginWork
阶段 - 它是去向下遍历一棵 fiber 树的一侧的子节点,然后遍历到叶子节点为止,以及 return 自己 child 的这种方式
- 在
performUnitOfWork
里面,还有一个方法叫做completeUnitOfWork
- 在
completeUnitOfWork
中会根据是否有中断,调用不同的一个处理方法 - 什么叫中断呢?
- 就是我们在渲染一个组件的时候,出现了错误的场景进行处理
- 或者优先级被更高优先级打断,低优先级转移到下一次处理
- 在
renderRoot
当中, 使用try catch
去调用workLoop
去循环每一个节点的 workLoop
里面调用的就是performUnitOfWork
- 在
performUnitOfWork
里面调用了beginWork
- 它里面就是执行各种不同组件的一个update的一个过程
- 在后面
completeUnitOfWork
,是处理到一侧的子节点遍历到最下层的时候,它没有子节点可以返回了 - 因为
beginWork
里面调用了各种update,它们都是return自己的child - 比如说,有这么一棵 fiber 树的一个情况的时候

- 我们先去对
RootFiber
执行了beginWork
,然后执行了之后 return 的是它的 child,也就是App
- 一层一层这么执行下来之后,到了有分叉节点,也就是说一个节点,下面有多个子节点的情况
- 它 return 的还是它的第一个 child 节点,所以这个时候,后面的节点是没有被执行到更新的
- 因为 inpput 它返回了之后,返回的是它里面的 input child 这个节点
- 到这里为止,这个 dom 的 input 节点,已经没有child了,这个时候就 return null
- 因为它的 child是 null,所以在这里首先执行的是
beginWork
- 如果有child,它就会一直往child方向进行一个查找
- 等到 next 等于 null 的情况的时候,就会去执行
completeUnitOfWork
completeUnitOfWork
就是对当前这个input界定 执行它的complete,具体执行什么- 在
beginWork
的过程当中,如果我们去update某一个节点的时候,有报错了 - 或者是 throw 了一个 promise, 就是 suspend 的情况, 它会执行一定的标记
- 在
renderRoot
当中,我们可以看到它如果有报错,它的catch是要在renderRoot
里面被捕获的这个错误 - 对这个错误补获之后,它是有一定的处理的, 处理完之后,它仍然处于这个
while
循环当中,它里面并没有 break- 位置:
packages/react-reconciler/src/ReactFiberScheduler.js#L1276
- 位置:
- 这个时候它还是会往下进行一个执行的,只不过它会在里面给我们当前这个节点去执行一个标记
- 在
completeUnitOfWork
的阶段,会对有各种不同标记的一个节点,执行不同的方法 - 主要的是我们正常流程走的是叫
completeWork
这么一个方法 - 而对于有异常的节点,它的一个流程是调用一个叫
unwindWork
的方法 - 然后会判断是否有兄弟节点来执行不同的操作
- 比如执行到上图 input 这个节点的
completeUnitOfWork
之后,它没有兄弟节点 - 它直接去执行它的父节点,也就是 Input 组件的
completeUnitOfWork
- 在这里,我们发现它是有兄弟节点的,因为第一次update的过程,是往下的
- 对于旁边的
List
节点是没有任何更新的, 这个时候我们就会返回它的兄弟节点List
- 比如执行到上图 input 这个节点的
- 对于这个兄弟节点, 继续对它执行一个update的一个过程,也就是
beginWork
的一个过程 - 这就是在
completeUnitOfWork
里面非常重要的一个判断条件- 就是如果有兄弟节点,它会直接返回这个兄弟节点
- 完成节点之后要赋值整个effect链
- effect是非常重要的,后面即将要把所有的更新结束的节点,挂载到真正的dom上面
- 这一个阶段叫做commit阶段,它要去执行每一个节点不同的跟dom有关的操作
- 在前面
beginWork
和completeUnitOfWork
,对每一个节点的更新过程标记了 - 有哪些 SideEffect 最终要被commit的一个过程
- 在
completeUnitOfWork
里面会完成这一步,把所有的effect节点进行一个串联 - 让
commitWork
的阶段可以非常方便的根据这个链去执行每一个节点的最终的操作
2 ) 源码
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L939
找到 completeUnitOfWork
// 这个API 是在 workInProgress 的基础上进行的一个操作
function completeUnitOfWork(workInProgress: Fiber): Fiber | null {// Attempt to complete the current unit of work, then move to the// next sibling. If there are no more siblings, return to the// parent fiber.// 进来首先就是一个 while true 的一个循环while (true) {// The current, flushed, state of this fiber is the alternate.// Ideally nothing should rely on this, but relying on it here// means that we don't need an additional field on the work in// progress.// 对于这个循环, 它首先获取currentconst current = workInProgress.alternate;if (__DEV__) {ReactCurrentFiber.setCurrentFiber(workInProgress);}// 然后 获取 returnFiber 和 siblingFiber,也就是它的父节点以及它的兄弟节点// 这个在后面我们判断是否要返回兄弟节点的时候就会用到const returnFiber = workInProgress.return;const siblingFiber = workInProgress.sibling;// 这里首先一进来就有一个大的判断,它是一个整个方法里面最大的一个判断// Incomplete 就是这个节点它是出现了错误,然后被捕获的, 并标记这个 sideEffect// 逻辑与操作来判断某一个属性上面它是否有某一个特性的一个方式if ((workInProgress.effectTag & Incomplete) === NoEffect) {if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {// Don't replay if it fails during completion phase.mayReplayFailedUnitOfWork = false;}// This fiber completed.// Remember we're completing this unit so we can find a boundary if it fails.nextUnitOfWork = workInProgress;if (enableProfilerTimer) {if (workInProgress.mode & ProfileMode) {startProfilerTimer(workInProgress);}nextUnitOfWork = completeWork(current,workInProgress,nextRenderExpirationTime,);if (workInProgress.mode & ProfileMode) {// Update render duration assuming we didn't error.stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);}} else {nextUnitOfWork = completeWork(current,workInProgress,nextRenderExpirationTime,);}if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {// We're out of completion phase so replaying is fine now.mayReplayFailedUnitOfWork = true;}stopWorkTimer(workInProgress);resetChildExpirationTime(workInProgress, nextRenderExpirationTime);if (__DEV__) {ReactCurrentFiber.resetCurrentFiber();}if (nextUnitOfWork !== null) {// Completing this fiber spawned new work. Work on that next.return nextUnitOfWork;}// 接下去这个个判断,是要构造一个所有 有 SideEffect 的节点的一个链状的结构// 这个链状结构最终是用于 commitWork 的时候用来进行对这些有 SideEffect的节点进行 commit 的一个操作,// 这边它的一个判断条件,returnFiber不等于null,并且returnFiber它不是一个 Incomplete 的一个节点// 因为对于一个 Incomplete 的节点,它唯一可以具有的一个SideEffect,就是这个节点已经被捕获了// 因为对于有 Incomplete 错误的节点是不会渲染正常的子节点的if (returnFiber !== null &&// Do not append effects to parents if a sibling failed to complete(returnFiber.effectTag & Incomplete) === NoEffect) {// Append all the effects of the subtree and this fiber onto the effect// list of the parent. The completion order of the children affects the// side-effect order.// 对于正常的一个情况, 首先要判断一下 returnFiber.firstEffect 是否等于 null// 符合判断就代表 现在这个 returnFiber 上还没有记录任何它的子节点的有副作用的子节点// 这个时候, 直接把当前节点的firstEffect赋值给 returnFiber.firstEffect// 因为它之前是没有任何一个的嘛,我们这边真正要做的是把当前节点的 firstEffect 到 lastEffect的一个链条// 这个单项链表,给它挂载到它的父节点的同样的一个 firstEffect到lastEffect的单项链表的最后if (returnFiber.firstEffect === null) {returnFiber.firstEffect = workInProgress.firstEffect;}// 就是下面这一段判断,就是来做这个事情的, 如果returnFiber.lastEffect不等于null,那说明它已经有了// 那么对于returnFiber上面有记录过别的 SideEffect 的节点之后// 我们当前节点是挂载到整个 SideEffect 链的最后,就是下面这样,就是把它连到最后的上面if (workInProgress.lastEffect !== null) {if (returnFiber.lastEffect !== null) {returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;}// 还要操作如下,因为它的这个(returnFiber.lastEffect)指针目前还指向它原来的那个lastEffect// 在 赋值 nextEffect之后,它的最后一个就是这个链的最后一个已经变成 workInProgress.lastEffect,所以这边要执行这么一个操作// 当然这个条件是要建立在 workInProgress.lastEffect 是有值的情况// 这是把它们各自的firsteffect到lasteffect,这个链给它进行一个串联的过程returnFiber.lastEffect = workInProgress.lastEffect;}// If this fiber had side-effects, we append it AFTER the children's// side-effects. We can perform certain side-effects earlier if// needed, by doing multiple passes over the effect list. We don't want// to schedule our own side-effect on our own list because if end up// reusing children we'll schedule this effect onto itself since we're// at the end.// 对于returnFiber来说,当前这个节点也可能是有副作用的,那么这边就接下去就会做这个操作// 如果当前节点的 effectTag > PerformedWork 的,因为 PerformedWork 是一个给 DEVTool 用的一个 sideEffect// 对于真正的react更新是没有任何意义的, 所以如果它仅仅只有 PerformedWork ,它就不是一个有效的 SideEffect 的节点const effectTag = workInProgress.effectTag;// Skip both NoWork and PerformedWork tags when creating the effect list.// PerformedWork effect is read by React DevTools but shouldn't be committed.if (effectTag > PerformedWork) {// 如果它有 SideEffect,就把当前节点作为父节点的 SideEffect 链的最后一个给它挂载上去// 或者如果是当前父节点没有任何记录的 SideEffect,它就是第一个if (returnFiber.lastEffect !== null) {returnFiber.lastEffect.nextEffect = workInProgress;} else {returnFiber.firstEffect = workInProgress;}returnFiber.lastEffect = workInProgress;}}if (__DEV__ && ReactFiberInstrumentation.debugTool) {ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);}if (siblingFiber !== null) {// If there is more work to do in this returnFiber, do that next.return siblingFiber;} else if (returnFiber !== null) {// If there's no more work in this returnFiber. Complete the returnFiber.workInProgress = returnFiber;continue;} else {// We've reached the root.return null;}} else {if (enableProfilerTimer && workInProgress.mode & ProfileMode) {// Record the render duration for the fiber that errored.stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);// Include the time spent working on failed children before continuing.let actualDuration = workInProgress.actualDuration;let child = workInProgress.child;while (child !== null) {actualDuration += child.actualDuration;child = child.sibling;}workInProgress.actualDuration = actualDuration;}// This fiber did not complete because something threw. Pop values off// the stack without entering the complete phase. If this is a boundary,// capture values if possible.const next = unwindWork(workInProgress, nextRenderExpirationTime);// Because this fiber did not complete, don't reset its expiration time.if (workInProgress.effectTag & DidCapture) {// Restarting an error boundarystopFailedWorkTimer(workInProgress);} else {stopWorkTimer(workInProgress);}if (__DEV__) {ReactCurrentFiber.resetCurrentFiber();}if (next !== null) {stopWorkTimer(workInProgress);if (__DEV__ && ReactFiberInstrumentation.debugTool) {ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);}// If completing this work spawned new work, do that next. We'll come// back here again.// Since we're restarting, remove anything that is not a host effect// from the effect tag.next.effectTag &= HostEffectMask;return next;}if (returnFiber !== null) {// Mark the parent fiber as incomplete and clear its effect list.returnFiber.firstEffect = returnFiber.lastEffect = null;returnFiber.effectTag |= Incomplete;}if (__DEV__ && ReactFiberInstrumentation.debugTool) {ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);}// 存在 sibling 节点, 注意这边是return,也就是跳出while循环if (siblingFiber !== null) {// If there is more work to do in this returnFiber, do that next.return siblingFiber;} else if (returnFiber !== null) {// If there's no more work in this returnFiber. Complete the returnFiber.// 如果returnFiber不等于null,那么 workInProgress = returnFiber// 比如对于最上面示例图的 input节点,它的 completeUnitOfWork// 如果它没有兄弟节点,那么它就继续执行Input的 completeUnitOfWork// 这个循环是在 completeUnitOfWork 内部进行的一个过程workInProgress = returnFiber;continue;} else {// 如果 returnFiber 也等于null,那么它直接 return null// 说明已经到达顶点了,就到达 RootFiber 了,我们的更新过程呢已经完成了// 只需要在接下去 commitRoot 就可以了return null;}}}// Without this explicit null return Flow complains of invalid return type// TODO Remove the above while(true) loop// eslint-disable-next-line no-unreachablereturn null;
}
- 对于这个while循环里面,其实它的所有代码都是在if else里面的
- if else它们最大的一个区别是什么呢?
- 在if里面我们可以看这边这个特殊情况
- 就是 nextUnitOfWork 等于 completeWork,然后传入一些属性
- 对于 else 的情况,这边调用了 next = unwindWork
- 这就是对于一个是否有抛出过错误的一个节点,执行的一个不同的操作
- completeWork,是没有任何一个错误的一个节点,它的一个complete的一个过程
- 调用了completework,就是把这个节点的更新给它完成了
- unwindWork 是有节点的错误,被捕获,如何去处理的一个过程
- 在上面的 SideEffect 到底有什么意义呢?
- 比如有如下场景:
- 有个列表上有值为 1, 2, 3的三个节点
- 点击 button 按钮,会让上面三个节点内的值乘上自身
- 对于2,3是4和9,对于1还是1
- 这里 1 是一个没有变化的过程
- 按照正常逻辑来说,只需要更新原本值是 2 和 3 的节点
- 实际上 react 里面也是这样做的
- 参考demo如下
import React, { Component } from 'react' import './App.css'class List extends Component {state = {a: 1,b: 2,c: 3,}handleClick = () => {this.setState(oldState => {const { a, b, c } = oldStatereturn {a: a * a,b: b * b,c: c * c,}})}render() {const { a, b, c } = this.statereturn [<span key="a">{a}</span>,<span key="b">{b}</span>,<span key="c">{c}</span>,<button key="button" onClick={this.handleClick}>click me</button>,]} }class Input extends Component {state = {name: 'wang',}handleChange = e => {// 这里如果使用方法设置`state`// 那么需要现在外面读取`e.target.value`// 因为在React走完整个事件之后会重置event对象// 以复用event对象,如果等到方法被调用的时候再读取`e.target.value`// 那时`e.target`是`null`this.setState({name: e.target.value,})}render() {return (<inputtype="text"style={{ color: 'red' }}onChange={this.handleChange}value={this.state.name}/>)} }class App extends Component {render() {return (<div className="main"><Input /><List /></div>)} }export default App
- 这个时候我们在 浏览器中针对 react-dom.development.js 在 commitRoot 上面打了一个断点
- 这个断点是看最终要进行commit的时候,它是如何去获取哪几个节点是要更新的
- 也就是我们的 SideEffect 链是怎么样的一个形式, 点一下,断点被捕获,捕获了之后
- 可以看到它接收的两个参数,一个是root,一个是finishedWork,那么它们其实是一个对应的关系
- root.current.alternate 就等于 finishedWork
- 因为 finishedWork 对应的是 workInProgress
- finishedWork 上面就会记录我们当前这一次更新,它所有需要去执行的,就是要更新到dom上面的一些内容
- 它的记录就在 firstEffect 到 lastEffect 的一个链上
- 它这边 firstEffect 记录的 elementType 是一个 span
- 所谓 effect 它最终是一个fiber节点,就是告诉我们哪一个fiber节点需要更新
- 对于我们这边第二个span,它的值是要被更新成4的
- 所以可以看到它的 firstEffect 的第一个节点是 span
- 它的 effectTag是 4,然后它的 updateQueen 的属性里面是一个数组
updateQueue: ["children", "4"]
- 这个大致意思可以看出,我们要把这个span标签对应的dom节点, 它的children里面显示的文字内容,从2变成4
- 还有就是下一个节点, 也是一个span, 看 nextEffect, 它的 elementType 也是span
- 它的 updateQueen 是
updateQueue: ["children", "9"]
,也就是原值为 3 的节点
- 对于commitWork,只会关心 firstEffect 到 lastEffect 这个链条上面的对应的 fiber 节点
- 它只需要更新的就是这两个dom节点的children的一个变化
- 这就是react里面的 vdom,它以最小化的程度去更新 dom
- 而不需要对其他的任何节点再进行一个操作,以此来提升一个性能
- 这个过程, 就是在
v16.6.3/packages/react-reconciler/src/ReactFiberScheduler.js#L1026
这个 if 里面被实现的 - 这个 demo 示例对应最最上面的流程图来说
- 这边点了 button之后,在这里创建了一个update
- 就是这个List上面, 通过调用 this.setState 去创建了这个 update
- 最终是要从 RootFiber 上面往下进行一个更新,更新到 List 中的第一个span,以及后续的span
- 然后发现只有第2和第3个span,它们的 children从 2 变成了 4,从 3 变成了 9
- 它们两个对应的 SideEffect 是
Update
对应值为4 (二进制的表示形式, 参考 ReactSideEffectTags.js),它们要把内容挂载到dom上面 - 第一个span的内容因为是1乘以1,所以它没有变化,就不需要执行真正的更新
- 首先执行
completeUnitOfWork
的是 第一个 span, 接下去它的 sibling - 因为第一个 span 没有 SideEffect,在这里,它的 returnFiber 就是 List
- 当前的 workInProgress 就是这第一个 span,这个span没有任何的 SideEffect
- 这个 List 自己也没有 SideEffect,所以赋值List的时候,对List也是没有任何的一个更新的, 如下
if (returnFiber.firstEffect === null) {returnFiber.firstEffect = workInProgress.firstEffect; }
- 这时候的 List 本身的 firstEffect 到 lastEffect是没有任何内容的,也就是null
- 进行到第2个span节点,它自己有 SideEffect, 而它没有子节点
- 所以它本身上面的 firstEffect到lastEffect是没有任何内容的
- 唯一需要操作的是要把它作为List的第一个 SideEffect 增加到这个链上面
- 因为是
Update
, 值为 4 肯定是大于Performedwork
值为 1的if (effectTag > PerformedWork) {if (returnFiber.lastEffect !== null) {returnFiber.lastEffect.nextEffect = workInProgress; // 情况1,标记为 L1, 以便下面引用说明} else {returnFiber.firstEffect = workInProgress; // 情况2, 标记为 L2}returnFiber.lastEffect = workInProgress; // 通用设定3,标记为 L3 }
- 它自身(workInProgress)要作为 returnFiber 的第一个 SideEffect
- 给它增加到 List 的firstEffect到lastEffect的链上
- 所以returnFiber的 firstEffect 和 lastEffect,都等于这个Fiber对象(第二个span) workInProgress
- 对应的执行语句是
L2
和L3
- 当第2个span执行complete完了,又要开始它的sibling, 也就是第三个span
- 第三个span也是有更新的,所以它也要增加到 List上面
- 这个时候List上面已经有变化,因为List.lastEffect(姑且这样表示)已经不等于null了
- 这个时候,执行的是 else 的情况,即
L1
- 因为在上一个节点的complete过程当中,已经指定了 firstEffect 和 lastEffect 都为 workInProgress(第2个span)
- 所以第二个span节点的 nextEffect 指向的是第三个span的节点
- 它们更新完之后又要更新 button,因为它没有任何的变化,所以 button 也没有更新
- 这个时候对于List来说它的 SideEffect 链是等于 span指向span的这么一个过程
- List的firstEffect指向第二个span对应的Fiber对象,lastEffect指向第三个span对应的Fiber对象
- List本身执行
completeUnitOfWork
的时候,自己没有任何的更新 - 因为它的state更新对于真正的dom操作,没有任何的关系,而且它也没有生命周期方法
- 所以 List 后期没有任何要做的事情了, 自己本身是没有 SideEffect,只有子节点的 SideEffect
- 在 List 执行
completeUnitOfWork
的时候, 代码中的 returnFiber 对应是 div - 而 workInProgress 对应的是 List, 而 List 要赋值它的 firstEffect 和 lastEffect
- 它的链指向returnFiber, 就是要放到 returnFiber 它的链的最后面
- returnFiber 这个 div,它目前是肯定没有任何的 SideEffect的, 所以它直接赋值成 workInProgress.firstEffect
- 也就是说,对于 div 它的 firstEffect 跟 lastEffect 已经变成 List 的 firstEffect 跟 lastEffect
- 因为上面节点都是没有任何更新的, 所以一层一层往上之后, 在
RootFiber
上面记录的 - firstEffect 跟 lastEffect 也就变成了最开始的两个span
- 所以这就是通过
completeUnitOfWork
,最终能够赋值到RootFiber
上面 - 在它的 firstEffect 到 lastEffect,单项链表上面去记录整个应用当中
- 所有需要去更新的最终的dom节点以及组件的fiber对象的一个过程
- 这里涉及到后期 commitRoot 的时候, 调用这个单项链表一个过程,这里先跳过
- 对于 unwindWork 的一个过程, 也是相似的, 先跳过 unwindWork 以及 completeWork
- 目前只关心它的一个遍历过程,跳过具体节点做的事情, 关注它的大致流程
- 它真正帮我们去实现了把所有需要更新的节点进行一个串联
- 让最终 commitRoot 的时候,能够方便获取到所有需要更新的节点的一个过程
相关文章:

React16源码: React中的completeUnitOfWork的源码实现
completeUnitOfWork 1 )概述 各种不同类型组件的一个更新过程对应的是在执行 performUnitOfWork 里面的 beginWork 阶段它是去向下遍历一棵 fiber 树的一侧的子节点,然后遍历到叶子节点为止,以及 return 自己 child 的这种方式在 performUni…...
uniapp移动端——企业微信H5调用jssdk实现扫一扫,通过weixin-java-cp获取ticket签名,配置config
背景: 使用企业微信开发扫一扫功能 可信域名验证 (1)企业微信的可信域名需要和企业微信的备案主体一致。 域名备案主体可通过站长工具查看域名备案主体。https://icp.chinaz.com/ 企业微信备案主体可以咨询管理员 (2)通过nginx配置域名归…...
【前端基础--1】
为后面爬虫打基础 使用Visual Studio Code(VS Code) https://code.visualstudio.com/#alt-downloads 网页基础 创建一个html网页 新建一个文件 文件名后缀.html 书写网页模板 html:5 回车键(或者Tab键)英文感叹号! 回…...

E2 Mysql的基本操作和用户权限
一、实验目的: 要求掌握Mysql平台的基本操作和基本的权限管理。 二、实验要求: 1、基本硬件配置:英特尔Pentium III 以上,大于4G内存; 2、软件要求:Mysql; 3、时间:4小时; 4、撰写实验报告并按时提交。 三、实验内容: Group 1: 安装Mys…...

TCP 的三次握手和四次挥手
Java 面试题 TCP 三次握手 第一次握手:客户端向服务端发送SYN包。报文中标志位SYN1,序列号seqx(x为随机整数)。此时客户端进入了 SYN_SEND 同步已发送状态。 第二次握手:服务端回复客户端SYNACK包。报文中标志位SYN1&…...

mybatisplus做SQL拦截添加自定义排序
前言 工作中写的一段代码,备个份,以后兴许能直接用 功能描述:如果前端传入了排序规则,则优先按传入的字段进行排序,SQL原有的排序规则追加到末尾 注:我们项目里的分页查询,是基于XML的SQL执行的…...

代码随想录算法训练营第29天(回溯算法05 | * 491.递增子序列 * 46.全排列 * 47.全排列 II
回溯算法part05 491.递增子序列解题思路与 90.子集II 不同的点回溯三部曲 46.全排列解题思路遇到的难点思考 47.全排列 II解题思路注意点拓展需要加深理解的点(需复习 小总结 491.递增子序列 本题和大家刚做过的90.子集II非常像,但又很不一样,…...

mac docker desktop被禁用了,如何使用虚拟机lima运行docker
安装lima brew install lima创建配置 echo "\\ndynamic:\n big-sur:\n image: docker://docker:git\n linux:\n image: docker.io/limasoftware/ubuntu:20.04 \\n" > ~/.lima/default.yaml启动名叫default的虚拟机 limactl start default测试 limactl …...

sublime text 开启vim模式
sublime text 开启vim模式 打开配置文件 mac下点击菜单栏 Sublime Text -> Settings... -> Settings 修改配置文件并保存 添加配置 // 开启vim模式 "ignored_packages": [// "Vintage", ], // 以命令模式打开文件 "vintage_start_in_comman…...
JS词法结构
编程语言的词法结构是一套基础性规则,用来描述如何使用这门语言来编写程序。作为语法的基础,它规定了诸如变量名是什么样的、怎么写注释,以及程序语句之间如何分隔等规则。 2.1程序的文本 JS区分大小写 JS忽略程序记号(token&am…...

程序媛的mac修炼手册-- 如何用Python节省WPS会员费
上篇分享了如何用微博爬虫,咱举例爬了女明星江疏影的微博数据。今天就用这些数据,给大家安利一下怎么用Python实现WPS中部分Excel付费功能。 MacOS系统自带的工具,绝大多数都非常顶,除Numbers外。当然,page比起word来&…...

ASP.NET Core NE8实现HTTP Upgrade和HTTP CONNECT代理服务器
看到一个文章[Go] 不到 100 行代码实现一个支持 CONNECT 动词的 HTTP 服务器 在NET8中如何实现 创建项目为MiniApi 编辑Program.cs文件。 var builder WebApplication.CreateSlimBuilder(args);var app builder.Build();// 将HTTP请求通过协议升级机制转为远程TCP请求&…...

apipost和curl收不到服务器响应的HTTP/1.1 404 Not Found
windows的apipost发送请求后,服务器响应了HTTP/1.1 404 Not Found,但是apipost一直显示发送中。 linux上的curl也一样。 使用wireshark抓包发现收到了响应,但是wireshark识别不了(图中是回应404后关闭了连接)ÿ…...
javascript:计算一个坐标数组的最小值点、最大值点、中心点
作者:CSDN _乐多_ 本文将介绍使用 javascript 语言计算一个坐标数组的最小值点、最大值点、中心点的代码。 文章目录 一、代码 一、代码 function calculateCenterPoint(points) {if (points.length 0) {return null;}let sumX 0;let sumY 0;let sumZ 0;for …...
使用远程工具连接Linux系统——使用Root用户登录
1、启动虚拟机,输入以下命令 进入root用户 sudo su或 su root修改ssh配置文件 vim /etc/ssh/sshd_config找到PermitRootLogin 并用#注释掉当前行 # PermitRootLogin prohibit-password添加: PermitRootLogin yes键入esc输入:wq保存退出 2、重启服…...

JuiceSSH结合内网穿透实现移动端设备公网远程访问Linux虚拟机
文章目录 1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 处于内网的虚拟机如何被外网访问呢?如何手机就能访问虚拟机呢? cpolarJuiceSSH 实现手机端远程连接Linux虚拟机(内网穿透,手机端连接Linux虚拟机) …...
06-枚举和模式匹配
上一篇:05-使用结构体构建相关数据 在本章中,我们将介绍枚举。枚举允许你通过枚举其可能的变体来定义一种类型。首先,我们将定义并使用一个枚举,以展示枚举如何与数据一起编码意义。接下来,我们将探索一个特别有用的枚…...

【C/C++】C/C++编程——C++ 开发环境搭建
C的开发环境种类繁多,以下是一些常见的C 集成开发环境: AppCode :构建与JetBrains’ IntelliJ IDEA 平台上的用于Objective-C,C,C,Java和Java开发的集成开发环境CLion:来自JetBrains的跨平台的C/C的集成开…...
Go 接口
接口概览 接口大概理解 接口类型是队其他类型行为的概括与抽象 接口类型中,包含函数声明,但没有数据变量接口的作用通过使用接口,可以写出更加灵活和通用的函数,这些函数不用绑定在一个特定的类型实现上Go 接口特征 很多面向对象…...

用 AI 将自拍照 P 进不同艺术作品,谷歌发布「艺术自拍 2」
1 月 24 日消息,谷歌旗下「艺术与文化」应用今日宣布,2018 年推出的「艺术自拍」功能在时隔近六年后,借助生成式 AI 的力量回归。官方表示,「艺术自拍 2」将再次使用户与艺术面对面,重新探访世界各地的艺术、历史和文化…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...