当前位置: 首页 > news >正文

React16源码: React中详解在渲染阶段Suspend的源码实现

Suspend 挂起详解


1 )概述

  • 在react的更新过程当中,它的任务是可以被挂起的,也就是 Suspend
  • 关于 Suspend
    • 字面意思就是挂起
    • 在某次更新的任务更新完成之后,暂时不提交
      • 在 react更新中,分为两个阶段,首先是render阶段
        • 主要就是包含 performUnitOfWork 以及 completeUnitOfWork
        • 对拿到的 reactElement 进行一个向下一层一层渲染
        • 这个过程呢叫做 beginWork, 在这个过程中
        • 从一个root节点开始渲染渲染到某一层的最终的一个子节点之后
        • 然后再由这个子节点往上返回,并且渲染它的兄弟节点
        • 最终回到root这个过程,然后把一个 reactElement 形成的树最终渲染成一棵完整的fiber树
        • 在这个过程当中,会根据传入的props以及每个节点的不同的类型来给它创建不同的实例
        • 以及进行一些 diff 的内容, 这个过程为什么要叫 render 阶段呢?
        • 因为它完全不会影响我们目前的整个 dom 树,它不会更新任何 dom 节点的特性
        • 在更新的过程当中,会形成一个effect的链
        • 这些effect的链就代表着在真正commit的阶段是要去把哪些dom节点进行一个更新
      • 在 render 阶段完成了之后,就进入commit阶段
        • 把render阶段能够发现的所有需要更新的节点提交到dom的更新上面,来完成一个UI的更新
        • 在 suspend 当中,完成了 render 阶段的所有任务, 但是暂时把这个任务停在 render 阶段不提交
        • 也就是把最终要修改 dom 的任务给它停掉,就不去做这个事情
        • 那么这个更新过程就叫做被 Suspend 的,也就是被挂起了
    • 这个更新可能在下次更新中再次被执行

2 )更新过程回顾

一开始

    root||  current↓RootFiber    -----alternate----->   workInProgress|                                       ||                                       |↓                                       ↓childFiber                            childWIP (childFiber 的 workInProgress)
  • 在执行更新的过程当中,一开始有一个root节点,这个root节点它是一个fiber对象
  • 它有一个属性叫 current 指向了 RootFiber
  • 这个 RootFiber 在更新完一次之后,它会有一个childFiber
  • 在要执行一个更新的过程当中,会把 rootFiber 去创建一个叫做 workInProgress 这么一个对象
  • 通过这个fiber对象的 alternate 属性去指向这个 workInProgress
  • 这个 workInProgress 跟 RootFiber 是两个完全不同的对象,只不过它们对象里面的有一些引用的属性是一样的
  • 比如说 memorizedState,还有 memorizedProps 这些常用的属性,然后在整个更新的过程当中
  • 都会通过 workInProgress 去创建它的 childFiber 的 workInProgress
  • 也就是说目前的 root 的 current 指向的这个 RouterFiber 以及它的childFiber
  • 跟在更新过程当中使用的 workInProgress 和 它的 childFiber 的 workInProgress 都是新的对象,不会相互影响

更新完成之后

    root\\\\\\\\\\\\ current\\\\\\\\ \ \ \ \ ↘RootFiber  -----alternate----->  workInProgress|                                       ||                                       |↓                                       ↓childFiber                            childWIP (childFiber 的 workInProgress)
  • 在整个更新完成之后,在执行了 commitRoot 之后,会做一个操作,叫做把 root.current 的指向变成 workInProgress
  • 这个时候因为已经把 workInProgress 上收集到的所有更新提交到 dom 上面了
  • 提交到 dom 上面之后,这个 workInProgress 跟 dom 的对应关系才是一一对应的
  • 而之前的rootFiber已经是一个过时的版本了, 如果当前的 root.current 还指向它的话,跟我们实际的dom的对应关系就不对了
  • 所以这时候,root 就直接修改它的 current 指向 workInProgress,就可以变成一个最新的状态
  • 这个 rootFiber 还会依然存在,存在于 workInProgress.alternate 上面
  • 后续要去继续更新的时候,又会从 workInProgress 上创建一个新的 workInProgress
  • 因为旧的 workInProgress 现在已经变成 root.current了,这个操作在哪里做呢?
    • 在commitRoot里面,完成了第二次commit,也就是去 commitAllLifeCycles 修改了所有dom的更新之后
    • 它会执行一句代码,叫做 root.current = finishedWork (packages/react-reconciler/src/ReactFiberScheduler.js#L730)
    • 这个 finishedWork 就是传入 commitRoot 的时候
    • 在render阶段更新完成的那个 workInProgress 对象
    • 需要注意它们两个对象 workInProgress 和 current,虽然是大部分属性都是一样的
    • 它们最大的区别就是两个完全独立的对象
    • 修改了 root.current 的指向之后,就代表 workInProgress 它的更新已经被提交了
    • 然后变成了一个新的状态,就存在 root.current 上面
  • 这就是在 react当中,有一个叫做 double buffer 的一个概念
  • 在更新完成之后,会修改root的指向来复用我们的workInProgress对象

3 )详解 Suspend

  • 这个过程完成之后,看下 Suspend
  • 在之前的 renderRoot, 在 workLoop 执行完成之后,会执行一堆判断

定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L1381

// Yield back to main thread.
if (didFatal) {const didCompleteRoot = false;stopWorkLoopTimer(interruptedBy, didCompleteRoot);interruptedBy = null;// There was a fatal error.if (__DEV__) {resetStackAfterFatalErrorInDev();}// `nextRoot` points to the in-progress root. A non-null value indicates// that we're in the middle of an async render. Set it to null to indicate// there's no more work to be done in the current batch.nextRoot = null;onFatal(root);return;
}if (nextUnitOfWork !== null) {// There's still remaining async work in this tree, but we ran out of time// in the current frame. Yield back to the renderer. Unless we're// interrupted by a higher priority update, we'll continue later from where// we left off.const didCompleteRoot = false;stopWorkLoopTimer(interruptedBy, didCompleteRoot);interruptedBy = null;onYield(root);return;
}// We completed the whole tree.
const didCompleteRoot = true;
stopWorkLoopTimer(interruptedBy, didCompleteRoot);
const rootWorkInProgress = root.current.alternate;
invariant(rootWorkInProgress !== null,'Finished root should have a work-in-progress. This error is likely ' +'caused by a bug in React. Please file an issue.',
);// `nextRoot` points to the in-progress root. A non-null value indicates
// that we're in the middle of an async render. Set it to null to indicate
// there's no more work to be done in the current batch.
nextRoot = null;
interruptedBy = null;if (nextRenderDidError) {// There was an errorif (hasLowerPriorityWork(root, expirationTime)) {// There's lower priority work. If so, it may have the effect of fixing// the exception that was just thrown. Exit without committing. This is// similar to a suspend, but without a timeout because we're not waiting// for a promise to resolve. React will restart at the lower// priority level.markSuspendedPriorityLevel(root, expirationTime);const suspendedExpirationTime = expirationTime;const rootExpirationTime = root.expirationTime;onSuspend(root,rootWorkInProgress,suspendedExpirationTime,rootExpirationTime,-1, // Indicates no timeout);return;} else if (// There's no lower priority work, but we're rendering asynchronously.// Synchronsouly attempt to render the same level one more time. This is// similar to a suspend, but without a timeout because we're not waiting// for a promise to resolve.!root.didError &&isYieldy) {root.didError = true;const suspendedExpirationTime = (root.nextExpirationTimeToWorkOn = expirationTime);const rootExpirationTime = (root.expirationTime = Sync);onSuspend(root,rootWorkInProgress,suspendedExpirationTime,rootExpirationTime,-1, // Indicates no timeout);return;}
}if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {// The tree was suspended.const suspendedExpirationTime = expirationTime;markSuspendedPriorityLevel(root, suspendedExpirationTime);// Find the earliest uncommitted expiration time in the tree, including// work that is suspended. The timeout threshold cannot be longer than// the overall expiration.const earliestExpirationTime = findEarliestOutstandingPriorityLevel(root,expirationTime,);const earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;}// Subtract the current time from the absolute timeout to get the number// of milliseconds until the timeout. In other words, convert an absolute// timestamp to a relative time. This is the value that is passed// to `setTimeout`.const currentTimeMs = expirationTimeToMs(requestCurrentTime());let msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;// TODO: Account for the Just Noticeable Differenceconst rootExpirationTime = root.expirationTime;onSuspend(root,rootWorkInProgress,suspendedExpirationTime,rootExpirationTime,msUntilTimeout,);return;
}// Ready to commit.
onComplete(root, rootWorkInProgress, expirationTime);
  • 比如说 if (didFatal) {},就是说有致命错误的时候,会执行 onFatal

    if (didFatal) {const didCompleteRoot = false;stopWorkLoopTimer(interruptedBy, didCompleteRoot);interruptedBy = null;// There was a fatal error.if (__DEV__) {resetStackAfterFatalErrorInDev();}// `nextRoot` points to the in-progress root. A non-null value indicates// that we're in the middle of an async render. Set it to null to indicate// there's no more work to be done in the current batch.nextRoot = null;onFatal(root);return;
    }
    
    • onFatal 是给 root.finishedWork,给它直接设为null,那么这个不算
  • 还有就是 if (nextUnitOfWork !== null) {}

    if (nextUnitOfWork !== null) {// There's still remaining async work in this tree, but we ran out of time// in the current frame. Yield back to the renderer. Unless we're// interrupted by a higher priority update, we'll continue later from where// we left off.const didCompleteRoot = false;stopWorkLoopTimer(interruptedBy, didCompleteRoot);interruptedBy = null;onYield(root);return;
    }
    
    • 这个情况是说这个更新过程是通过 reactScheduler 它进行时间片的更新的一个过程
    • 而这个root的更新又因为比较的长,一个时间片没更新完, 这个时候跳出更新的时候,它就是 onYield
    • 它下一次等浏览器执行完动画之类的操作之后,有新的一个时间片进来的时候,我们会继续在 nextUnitOfWork 上面进行一个更新
    • 这就是类似于中断,再继续的一个流程,这个跟 suspend 其实也没有什么关系
    • 真正跟 suspend 有关的是下面几个
  • if(nextRenderDidError),会把提交放到第一优先级的任务上

    if (nextRenderDidError) {// There was an errorif (hasLowerPriorityWork(root, expirationTime)) {// There's lower priority work. If so, it may have the effect of fixing// the exception that was just thrown. Exit without committing. This is// similar to a suspend, but without a timeout because we're not waiting// for a promise to resolve. React will restart at the lower// priority level.markSuspendedPriorityLevel(root, expirationTime);const suspendedExpirationTime = expirationTime;const rootExpirationTime = root.expirationTime;onSuspend(root,rootWorkInProgress,suspendedExpirationTime,rootExpirationTime,-1, // Indicates no timeout);return;} else if (// There's no lower priority work, but we're rendering asynchronously.// Synchronsouly attempt to render the same level one more time. This is// similar to a suspend, but without a timeout because we're not waiting// for a promise to resolve.!root.didError &&isYieldy) {root.didError = true;const suspendedExpirationTime = (root.nextExpirationTimeToWorkOn = expirationTime);const rootExpirationTime = (root.expirationTime = Sync);onSuspend(root,rootWorkInProgress,suspendedExpirationTime,rootExpirationTime,-1, // Indicates no timeout);return;}
    }
    
    • 也就是说, 这边有一个判断 if(hasLowerPriorityWork) {}, 如果它是有一个低优先级的任务,还没有被更新的
    • 这个时候调用了 markSuspendedPriorityLevel,这代表要把当前的这一次更新的内容去给它 suspend 了
    • 然后标记这个更新被 suspend了, 它会调用一个叫做 onSuspend 的方法
      // packages/react-reconciler/src/ReactFiberScheduler.js#L1954
      function onSuspend(root: FiberRoot,finishedWork: Fiber,suspendedExpirationTime: ExpirationTime,rootExpirationTime: ExpirationTime,msUntilTimeout: number,
      ): void {root.expirationTime = rootExpirationTime;// !shouldYieldToRenderer() 表示任务还没有超时// 并且 msUntilTimeout === 0 直接设置了 finishedwork// 这个时候最终会直接调用 commitRootif (msUntilTimeout === 0 && !shouldYieldToRenderer()) {// Don't wait an additional tick. Commit the tree immediately.root.pendingCommitExpirationTime = suspendedExpirationTime;root.finishedWork = finishedWork;} else if (msUntilTimeout > 0) {// Wait `msUntilTimeout` milliseconds before committing.// 这个 scheduleTimeout 就是 window.setTimeout 方法root.timeoutHandle = scheduleTimeout(onTimeout.bind(null, root, finishedWork, suspendedExpirationTime),msUntilTimeout,);}
      }
      
      • 进入 onTimeout
        function onTimeout(root, finishedWork, suspendedExpirationTime) {// The root timed out. Commit it.root.pendingCommitExpirationTime = suspendedExpirationTime;root.finishedWork = finishedWork; // 注意,这里// Read the current time before entering the commit phase. We can be// certain this won't cause tearing related to batching of event updates// because we're at the top of a timer event.recomputeCurrentRendererTime();currentSchedulerTime = currentRendererTime;flushRoot(root, suspendedExpirationTime); // flushRoot 强制调用 commitRoot
        }
        
        • 进入 flushRoot
          function flushRoot(root: FiberRoot, expirationTime: ExpirationTime) {invariant(!isRendering,'work.commit(): Cannot commit while already rendering. This likely ' +'means you attempted to commit from inside a lifecycle method.',);// Perform work on root as if the given expiration time is the current time.// This has the effect of synchronously flushing all work up to and// including the given time.nextFlushedRoot = root;nextFlushedExpirationTime = expirationTime;performWorkOnRoot(root, expirationTime, false); // 在这里面,存在 finishedWork 则直接调用 `completeRoot`// Flush any sync work that was scheduled by lifecyclesperformSyncWork();
          }
          
          • 是否要调用completeRoot, 都是通过判断 root.finishedWork 来完成的
          • 只要 root 它有已经完成的更新的工作,那么它就要去调用 commitRoot 来进行一个commit
          • 而通过给 root.finishedWork设置成null,就是告诉后续的代码,没有任务要提交
          • 所以不会执行 completeRoot, 所以不会 commitRoot
      • 看到这个方法,它只是设置了 root.expirationTime = rootExpirationTime
      • 然后接下去, 因为传进来的 msUnitlTimeout 是 -1,所以接下去这两个判断是都不符合的
      • 也就是说它接下去什么任务都没有做,而且它也没有去调用 commitRoot
      • 那么这个更新流程不就是被白费了吗?事实上也确实是如此的, 这边回头看 react 上写的相关注释
        // There's lower priority work. If so, it may have the effect of fixing
        // the exception that was just thrown. Exit without committing. This is
        // similar to a suspend, but without a timeout because we're not waiting
        // for a promise to resolve. React will restart at the lower
        // priority level.
        
      • 如果这边有低优先级的任务,就不提交这次更新,因为没有提交这个更新
      • 而且在 current 上面它的 updateQueen 是没有被删除的
      • 也就是说这些 update 它依然存在, 这些 update 存在的话
      • 在下一次低优先级的任务去执行更新的时候,它依然会执行update
      • 这个时候它有可能可以修复在这一次渲染当中出现的问题
      • 所以只要在之前的渲染当中出现了错误,而且有低优先级的任务在
      • react会直接不提交,而是把这个提交的更新放到低优先级的任务上,再去渲染一次
  • 如果没有低优先级的任务,react 会直接发起一个新的同步更新

  • 就是在 else if 下面, 这边的判断条件是比较苛刻的 else if(!root.didError && isYieldy) {}

    else if (// There's no lower priority work, but we're rendering asynchronously.// Synchronsouly attempt to render the same level one more time. This is// similar to a suspend, but without a timeout because we're not waiting// for a promise to resolve.!root.didError &&isYieldy
    ) {root.didError = true;const suspendedExpirationTime = (root.nextExpirationTimeToWorkOn = expirationTime);const rootExpirationTime = (root.expirationTime = Sync);onSuspend(root,rootWorkInProgress,suspendedExpirationTime,rootExpirationTime,-1, // Indicates no timeout);return;
    }
    
    • 在没有错误和存在需要被中断和恢复的任务的条件下,可以再去发起一次,
    • 如果符合这个条件,会设置 root.expirationTime = (root.exirationTime = Sync)
    • 下一次更新的过程,会走 Sync 的流程
    • 不会走时间片更新,而会强制进行一个同步的直接更新以及渲染的过程
    • 同时,这边又设置了 root.nextExpirationTimeToWorkOn = expirationTime
    • 这个 expirationTime 就是这一次 renderRoot 的时候,它的 nextExpirationTimeToWorkOn
      • 在上面,一开始进来的时候就设置 const expirationTime = root.nextExpirationTimeToWorkOn
      • 参考 packages/react-reconciler/src/ReactFiberScheduler.js#L1217
    • 然后它调用了 onSuspend, 这里注意传入的是 -1,所以不会做任何的事情
    • 为何要设置 root.expirationTime = (root.exirationTime = Sync)
      • 在 performWork 里面,在执行 performWorkOnRoot, 在这个函数里面会有这么一段
        renderRoot(root, isYieldy); // 这边调用 renderRoot 返回了
        finishedWork = root.finishedWork;
        // 如果没有 finishedWork 就不会执行 completeRoot
        if (finishedWork !== null) {// We've completed the root. Commit it.completeRoot(root, finishedWork, expirationTime);
        }
        
      • 在 performWork 里面,在执行完 performWorkOnRoot 之后, 会有这么一段
        performWorkOnRoot(nextFlushedRoot,nextFlushedExpirationTime,currentRendererTime > nextFlushedExpirationTime,
        );
        findHighestPriorityRoot();
        
      • 执行 findHighestPriorityRoot 会去再次找一次优先级最高的 root
      • 找优先级最高的root这个方法,是根据 expirationTime 的大小来进行一个判断的
      • 在这个过程当中,Sync 的 expirationTime 的优先级是最高的
      • 而在 renderRoot 里面设置了这个 root.exirationTime = Sync
      • 说明这个 root 会立马就被执行一个更新, 因为那边的循环它是没有结束的
      • 所以这边强制发起了一次对这个 nextExpirationTimeToWorkOn
      • 优先级的任务进行一次同步的更新这么一个操作,来强制再次进行更新
      • 通过这样来重新渲染一次,看一下是否在重新渲染的过程当中能够解决前一次渲染当中出现的这个错误
      • 一开始 root.didError 肯定是 false,所以这边是可以符合条件的
      • 这边在发起同步过程中,设置它的 didError 为 true
      • 这样的话,如果这一次同步更新,它依然没有完成任务的话,再后来是进不来这个判断的,它还是会被强制提交的
      • 这两种的任务都是被挂起的, 而且可以发现的是,被挂起的任务是根本不会走 commitRoot 的流程的
      • 也就是说这个 render 更新流程直接被抛弃了,我们的 workInProgress 已经没有用了
      • 因为下一次要重新进行render的时候,我们的 workInProgress 也就是 nextUnitOfWork 是会通过 nextRoot.current 来进行创建的
        // packages/react-reconciler/src/ReactFiberScheduler.js#L1230
        nextUnitOfWork = createWorkInProgress(nextRoot.current,null,nextRenderExpirationTime,
        );
        
      • 这个时候因为直接 onSuspend 了,没有执行commit
      • 这个时候 root.current 指针是没有改变的
      • 所以它还会从老的状态上重新发起一次更新,这就是 onSuspend 任务挂起它的一个意思
  • 对于Suspend 的来说,最大的一个情况,就是在下面这个情况下面, if (!isExpired && nextLatestAbsoluteTimeoutMs !== -1) {}

    if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {// The tree was suspended.const suspendedExpirationTime = expirationTime;markSuspendedPriorityLevel(root, suspendedExpirationTime);// Find the earliest uncommitted expiration time in the tree, including// work that is suspended. The timeout threshold cannot be longer than// the overall expiration.const earliestExpirationTime = findEarliestOutstandingPriorityLevel(root,expirationTime,);const earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;}// Subtract the current time from the absolute timeout to get the number// of milliseconds until the timeout. In other words, convert an absolute// timestamp to a relative time. This is the value that is passed// to `setTimeout`.const currentTimeMs = expirationTimeToMs(requestCurrentTime());let msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;// TODO: Account for the Just Noticeable Differenceconst rootExpirationTime = root.expirationTime;onSuspend(root,rootWorkInProgress,suspendedExpirationTime,rootExpirationTime,msUntilTimeout,);return;
    }
    
    • 这边会执行一个 onSuspend,并且会传入一个 msUntilTimeout
    • 就这个 timeoutout,可以看上面这个条件,msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout
  • 如果以上条件都不满足,不再发起新的同步更新,就会直接走到后面准备进入commit阶段

    // Ready to commit.
    onComplete(root, rootWorkInProgress, expirationTime);
    
    • 调用 onComplete, 而 onComplete,就会调用 commitRoot

总结3种 Suspend 的场景

  • 1 )把优先级放到低优先级的任务上
    • 会把当前 render 直接废弃,让低优先级的任务
    • 再次去渲染这些更新来查看他是否可以把错误解决掉
  • 2 )直接发起一个新的同步更新
    • 没有低优先级的任务,会重新发起的是同步更新来强制再次去渲染一次来看是否可以解决这个问题
    • 如果不能解决,那么就代表不能解决这个问题,只能按照错误的方式去把内容渲染出来
  • 3 )设置timeout然后提交
    • 只有在 throw 的是 Promise 的情况
    • 也就是通过 Suspense 这个功能去实现 throw 一个 Promise
    • 然后等到 Promise 解决之后再去渲染新的内容的一个情况
    • 这种情况会设置 timeout,这里先跳过

相关文章:

React16源码: React中详解在渲染阶段Suspend的源码实现

Suspend 挂起详解 1 &#xff09;概述 在react的更新过程当中&#xff0c;它的任务是可以被挂起的&#xff0c;也就是 Suspend关于 Suspend 字面意思就是挂起在某次更新的任务更新完成之后&#xff0c;暂时不提交 在 react更新中&#xff0c;分为两个阶段&#xff0c;首先是re…...

mac电脑风扇控制软件:Macs Fan Control Pro for mac 激活版

Macs Fan Control 是一款专门为 Mac 用户设计的软件&#xff0c;它可以帮助用户控制和监控 Mac 设备的风扇速度和温度。这款软件允许用户手动调整风扇速度&#xff0c;以提高设备的散热效果&#xff0c;减少过热造成的风险。 Macs Fan Control 可以在菜单栏上显示当前系统温度和…...

easyexcel解析跨多行的数据

在使用easyexcel解析excel文件的时候&#xff0c;存在某列横跨多行&#xff0c;那么存在解析出的对象的某些属性是没有值的&#xff0c;那么我们要怎么处理呢&#xff1f;代码如下 定义实体对应excel文件 public class EtcParkingReconciliationDailyImportModel implements S…...

双目相机立体匹配基础

双目匹配就是用左相机和右相机去拍摄同一个点&#xff0c;目的是找到三维世界的同一个点&#xff0c;也就是在左相机和右相机中的成像点之间的像素差&#xff08;视差&#xff09;&#xff0c;根据视差去求解深度&#xff0c;那么找到左相机点到右相机的同一个对应点这个过程就…...

【图论】网络流

网络流目前只整理模板&#xff0c;学习的话这篇博客可能不太适合 代码参考下方博客&#xff0c;加了一些自己的注释 算法学习笔记(28): 网络流究级的最大流算法&#xff1a;ISAP与HLPP FF 和 EK 仅用作理解代码&#xff0c;赛时请使用 Dinic 或 ISAP 下文建图方式都基于链式…...

【Matplotlib】figure方法 你真的会了吗!?

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;matplotlib &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…...

[C++]继承(续)

一、基类和派生类对象赋值转换 在public继承时&#xff0c;父类和子类是一个“is - a”的关系。 子类对象赋值给父类对象/父类指针/父类引用&#xff0c;我们认为是天然的&#xff0c;中间不产生临时对象&#xff0c;也叫作父子类赋值兼容规则&#xff08;切割/切片&#xff…...

恒创科技:服务器内存不足影响大吗?

​  服务器在为网站、应用程序和在线服务提供支持方面发挥着关键作用。这些服务器需要提供最佳性能&#xff0c;以确保正常无缝的用户体验&#xff0c;而RAM是显著影响服务器性能的关键配置之一。 RAM 是一种随机存取存储器&#xff0c;计算机和服务器使用它来临时存储正在使…...

深入理解网络通信和TCP/IP协议

目录 计算机网络是什么&#xff1f; 定义和分类 计算机网络发展简史 计算机网络体系结构 OSI 七层模型 TCP/IP 模型 TCP/IP 协议族 TCP/IP 网络传输中的数据 地址和端口号 MAC地址 IP 地址 端口号 为什么端口号有65535个&#xff1f; 综述 TCP 特性 TCP 三次握…...

Open CASCADE学习|分割曲线

1、通过参数进行分割 分别获得曲线的 FirstParameter 和 LastParameter &#xff0c;然后对参数进行分割&#xff0c;获得n个ui&#xff0c;并对每个ui调用D0&#xff08;获得这个点的坐标值&#xff09;或D1&#xff08;获得这个点的坐标值和切向量&#xff09;。这个方法的优…...

vulhub中Adminer远程文件读取漏洞复现(CVE-2021-43008)

Adminer是一个PHP编写的开源数据库管理工具&#xff0c;支持MySQL、MariaDB、PostgreSQL、SQLite、MS SQL、Oracle、Elasticsearch、MongoDB等数据库。 在其版本1.12.0到4.6.2之间存在一处因为MySQL LOAD DATA LOCAL导致的文件读取漏洞。 参考链接&#xff1a; https://gith…...

MOS管驱动电流估算-Qg参数

MOS管驱动电流估算 例&#xff1a;FDH45N50F如下参数&#xff1a; 有人可能会这样计算&#xff1a; 开通电流 带入数据得 关断电流 带入数据得 于是乎得出这样的结论&#xff0c;驱动电流只需 250mA左右即可。仔细想想这样计算对吗&#xff1f; 这里必须要注意这样一个条件细…...

Vision Transfomer系列第一节---从0到1的源码实现

本专栏主要是深度学习/自动驾驶相关的源码实现,获取全套代码请参考 这里写目录标题 准备逐步源码实现数据集读取VIt模型搭建hand类别和位置编码类别编码位置编码 blocksheadVIT整体 Runner(参考mmlab)可视化 总结 准备 本博客完成Vision Transfomer(VIT)模型的搭建和flowers数…...

【CSS + ElementUI】更改 el-carousel 指示器样式且隐藏左右箭头

需求 前三条数据以走马灯形式展现&#xff0c;指示器 hover 时可以切换到对应内容 实现 <template><div v-loading"latestLoading"><div class"upload-first" v-show"latestThreeList.length > 0"><el-carousel ind…...

Ubuntu 22.04 上安装和使用 Go

1.下载  All releases - The Go Programming Language //https://golang.google.cn/dl/wget https://golang.google.cn/dl/go1.21.6.linux-amd64.tar.gz 2.在下载目录下执行&#xff0c;现在&#xff0c;使用以下命令将文件提取到 “/usr/local ” 位置 sudo tar -C /usr/…...

ES6-const

一、基本用法 - 语法&#xff1a;const 标识符初始值;注意:const一旦声明变量&#xff0c;就必须立即初始化&#xff0c;不能留到以后赋值 - 规则&#xff1a;1.const 声明一个只读的常量&#xff0c;一旦声明&#xff0c;常量的值就不能改变2.const 其实保证的不是变量的值不…...

Android消息通知Notification

Notification 发送消息接收消息 #前言 最近在做消息通知类Notification的相关业务&#xff0c;利用闲暇时间总结一下。主要分为两部分来记录&#xff1a;发送消息和接收消息。 发送消息 发送消息利用NotificationManager类的notify方法来实现&#xff0c;现用最普通的方式发…...

2V2无人机红蓝对抗仿真

两架红方和蓝方无人机分别从不同位置起飞&#xff0c;蓝方无人机跟踪及击毁红方无人机 2020a可正常运行 2V2无人机红蓝对抗仿真资源-CSDN文库...

VUE3语法--computed计算属性中get和set使用案例

1、功能概述 计算属性computed是Vue3中一个响应式的属性,最大的用处是基于多依赖时的监听。也就是属性A的值可以根据其他数据的变化而响应式的变化。 在Vue3中,你可以使用computed函数来定义计算属性。computed函数接收两个参数:一个包含getter和setter函数的对象和可选的…...

Linux cd 和 df 命令执行异常

这篇记录一些奇奇怪怪的命令执行异常的情况&#xff0c;后续有新的发现也会补录进来 情况一 /tmp 目录权限导致 按 tab 补充报错 情况描述 cd 按 tab 自动补充文件报错&#xff08;普通用户&#xff09; bash: cannot create temp file for here-document: Permission denie…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...