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,这里先跳过

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

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

相关文章

16:JSP简介、注释与Scriptlet、Page指令元素、Include操作、内置对象、四种属性-Java Web

在Java Web开发领域&#xff0c;JavaServer Pages&#xff08;JSP&#xff09;作为一种动态网页技术&#xff0c;在构建高效Web应用程序中发挥着核心作用。本文将详细介绍JSP的基础概念&#xff0c;包括其基本结构、注释方法、Scriptlet的使用、Page指令元素的功能以及Include包…

回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小二乘支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小二乘支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小二乘支持向量机的数据多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于OOA-LSSVM鱼鹰算法…

Python||五城P.M.2.5数据分析与可视化_使用华夫图分析各个城市的情况(下)

目录 沈阳市的空气质量 华夫图 柱状图 总结 五城P.M.2.5数据分析与可视化——北京市、上海市、广州市、沈阳市、成都市&#xff0c;使用华夫图和柱状图分析各个城市的情况 沈阳市的空气质量 华夫图 import numpy as np import pandas as pd import matplotlib.pyplot as plt …

Servlet(java web基础篇)

Servlet&#xff08;java web基础篇&#xff09; Servlet是Java提供的一门动态web资源开发技术,不同的用户访问看到的效果不一样。Servlet是lavaEE规范之一&#xff0c;其实就是一个接口&#xff0c;将来我们需要定义Servlet类实现Servlet:接口&#xff0c;并由web服务器运行S…

TorchVision的使用方法、更改默认路径

TorchVision的使用 1. 转换和增强图像 torchvision.transforms.v2 参数作用Resize将输入调整为给定大小RandomShortestSize随机调整输入的大小RandomResize随机调整输入的大小RandomCrop在随机位置裁剪输入RandomResizedCrop裁剪输入的随机部分并将其调整为给定大小RandomIoU…

Java生成微信小程序二维码的方式有哪些?

大家好我是咕噜美乐蒂&#xff0c;很高兴又见面啦&#xff01;今天我们来谈一下如何使用Java生成微信小程序二维码&#xff0c;有哪些方式方法呢&#xff1f; 生成微信小程序二维码是开发微信小程序时的常见需求之一。在Java中&#xff0c;我们可以使用多种方式来生成微信小程…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-11-playwright操作iframe-上篇

1.简介 原估计宏哥这里就不对iframe这个知识点做介绍和讲解了&#xff0c;因为前边的窗口切换就为这种网页处理提供了思路&#xff0c;另一个原因就是虽然iframe很强大&#xff0c;但是现在很少有网站用它了。但是还是有小伙伴或者童鞋们私下问这个问题&#xff0c;那么宏哥就…

【后端开发】正向代理与反向代理

正向代理 正向代理&#xff08;forward proxy&#xff09;&#xff1a;是一个位于客户端和目标服务器之间的服务器(代理服务器)&#xff0c;为了从目标服务器取得内容&#xff0c;客户端向代理服务器发送一个请求并指定目标&#xff0c;然后代理服务器向目标服务器转交请求并将…

【日志记录】——主MCU 通过私有协议更新从MCU程序固件

一&#xff1a;需求分析 在一些系统较为复杂的嵌入式设备中&#xff0c;往往不止一片MCU或者处理模块&#xff0c;通常为一片主MCU负责应用逻辑处理和对外网络通信&#xff0c;其他从MCU负责实时采集处理高频数据&#xff0c;在设备运营过程中&#xff0c;往往伴随新需求或者bu…

动态内容推荐系统PHP代码

这个系统旨在根据用户行为和偏好&#xff0c;动态地向用户推荐内容。这样的系统可以应用于新闻网站、社交媒体平台、电子商务网站等多种场景&#xff0c;以提升用户体验和参与度。 ### 功能概述 系统将基于用户的浏览历史、点击行为和时间花费等数据来评估用户的兴趣点&#…

BUUCTF-Real-[ThinkPHP]2-Rce1

任意代码执行漏洞 ThinkPHP 2.x版本中&#xff0c;使用preg_replace的/e模式匹配路由&#xff1a; $res preg_replace((\w).$depr.([^.$depr.\/])e, $var[\\\1\]"\\2";, implode($depr,$paths)); 导致用户的输入参数被插入双引号中执行&#xff0c;造成任意代码执行…

Open CASCADE学习|拓扑变换

目录 平移变换 旋转变换 组合变换 通用变换 平移变换 TopoDS_Shape out;gp_Trsf theTransformation;gp_Vec theVectorOfTranslation(0., 0.125 / 2, 0.);theTransformation.SetTranslation(theVectorOfTranslation);BRepBuilderAPI_Transform myBRepTransformation(out, th…

go 中的范型

泛型是随着Go 1.18版本发布的。它基本上意味着参数化的类型&#xff0c;也就是说&#xff0c;它允许程序员在写代码时&#xff0c;类型可以稍后指定&#xff0c;因为类型在当时并不相关。换句话说&#xff0c;在编写一些代码时&#xff0c;你不提供数值的类型。这些类型的值会在…

Leetcode刷题笔记题解(C++):36. 有效的数独

思路一&#xff1a;暴力破解&#xff0c;两个二维数组记录行、列对应的数字出现的次数&#xff0c;比如rows[i][index]表示的数字index在i行出现的次数&#xff0c;三维数组记录每个块中对应数字出现的次数&#xff0c;比如boxes[i/3][j/3][index]表示的数字index在[i/3][j/3]个…

校园墙表白墙系统uniapp微信小程序

配置文件 (自动编号、配置参数名称、配置参数值)&#xff1b; 前端开发:vue 语言&#xff1a;javapythonnodejsphp均支持 运行软件:idea/eclipse/vscode/pycharm/wamp均支持 框架支持:Ssm/django/flask/thinkphp/springboot/springcloud均支持 数据库 mysql 数据库工具&#x…

定时器 Timer(超详细模拟实现)

目录 一、定时器 1.定时器概述 2.Java标准库提供的定时器类 3.定时器代码样例 二、实现 1.实现思路 2.代码实现 2.1纯享版 2.2注释版 3.代码解析(超详细) 3.1描述类MyTimerTask ①构造&#xff1a;MyTimerTask&#xff08;Runnable runnable, long delay&#xff…

如何使用本地私有NuGet服务器

写在前面 上一篇介绍了如何在本地搭建一个NuGet服务器&#xff0c; 本文将介绍如何使用本地私有NuGet服务器。 操作步骤 1.新建一个.Net类库项目 2.打包类库 操作后会生成一个.nupkg文件&#xff0c;当然也可以用dotnet pack命令来执行打包。 3.推送至本地NuGet服务器 打开命…

vue3 封装WebSocket(直接复制)

创建文件WebSocket.js 只有基本的功能 import { ref } from vue; import store from "/store";export default function useWebSocket(url) {const messagesData ref([]);// 创建WebSocket连接和相关方法const socket new WebSocket(url);socket.onopen () >…

《计算机网络简易速速上手小册》第6章:网络性能优化(2024 最新版)

文章目录 6.1 带宽管理与 QoS - 让你的网络不再拥堵6.1.1 基础知识6.1.2 重点案例&#xff1a;提高远程办公的视频会议质量实现步骤环境准备Python 脚本示例注意事项 6.1.3 拓展案例1&#xff1a;智能家居系统的网络优化实现思路Python 脚本示例 6.1.4 拓展案例2&#xff1a;提…

计算机网络_1.5 计算机网络的性能指标

1.5 计算机网络的性能指标 一、总览二、常用的八个计算机网络性能指标1、速率&#xff08;1&#xff09;数据量&#xff08;2&#xff09;速率&#xff08;3&#xff09;数据量与速率中K、M、G、T的数值辨析&#xff08;4&#xff09;【练习1】计算发送数据块的所需时间 2、带宽…