当前位置: 首页 > 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…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

基于鸿蒙(HarmonyOS5)的打车小程序

1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...