React16源码: React中处理LegacyContext相关的源码实现
LegacyContext
- 老的 contextAPI 也就是我们使用 childContextTypes 这种声明方式
- 来从父节点为它的子树提供 context 内容的这么一种方式
- 遗留的contextAPI 在 react 17 被彻底移除了,就无法使用了
- 那么为什么要彻底移除这个contextAPI的使用方式呢?
- 因为它对性能的影响会比较的大,它会影响整个子树的一个更新过程
- 它嵌套的context提供者是需要进行一个数据的合并的
- 嵌套组件中,如果父子两者都提供相同的变量,会进行一个合并
- 越接近里层,越会被选择
- 也就是说在孙子消费变量的时候,选择父亲的,舍弃爷爷的
2 )源码
定位到 packages/react-reconciler/src/ReactFiberBeginWork.js#L1522
在这里,有这个判断 if ( oldProps === newProps && !hasLegacyContextChanged() && updateExpirationTime < renderExpirationTime ) {}
看到有
import {hasContextChanged as hasLegacyContextChanged
} from './ReactFiberContext';
关注 hasLegacyContextChanged
基于此,定位到 packages/react-reconciler/src/ReactFiberContext.js#L115
// 要去推入 stack 的值的时候,就要去创建这么一个 cursor 来标记不同类型的一个值
function hasContextChanged(): boolean {return didPerformWorkStackCursor.current;
}
回顾到 context-stack 中
// packages/react-reconciler/src/ReactFiberContext.js#L36// A cursor to the current merged context object on the stack.
// 用来记录我们当前的我们更新到某一个节点之后,它应该可以拿到的context对应的所有值
let contextStackCursor: StackCursor<Object> = createCursor(emptyContextObject);// A cursor to a boolean indicating whether the context has changed.
// 代表着我们在更新到某一个节点的时候,它这个context是否有变化这么一个情况
let didPerformWorkStackCursor: StackCursor<boolean> = createCursor(false);
再次回到 ReactFiberBeginWork.js 中的 if ( oldProps === newProps && !hasLegacyContextChanged() && updateExpirationTime < renderExpirationTime ) {}
对于在 beginWork 中的 context 操作,可以看上述判断成立的条件下的代码,即便有符合跳过更新的操作,依然 需要 push 和 pop 操作
进入代码
switch (workInProgress.tag) {case ClassComponent: {const Component = workInProgress.type;// 如果当前组件是一个 provider 则进行 push 操作if (isLegacyContextProvider(Component)) {pushLegacyContextProvider(workInProgress);}break;}
}
-
跟遗留的 contextAPI 有关,通过 legency 标志,如果一个组件能够作为一个 context 的提供者
-
那么它肯定是一个
ClassComponent
, 因为要通过 getchildcontext 这么一个方法来声明我们子树当中提供了哪些 concontext -
最主要的就是来看在classcomponent的更新过程当中,如果它是一个contextprovider,那么它要执行的操作是
pushLegacyContextProvider
-
进入
isLegacyContextProvider
, 看到它是isContextProvider
的别名// 这个 type 就是组件实例,这个 childContextTypes function isContextProvider(type: Function): boolean {// 通过判断应用中声明的 class 上面是否有这个属性const childContextTypes = type.childContextTypes;return childContextTypes !== null && childContextTypes !== undefined; }
- 通过声明的这个class给它挂载 childContextTypes 来表示它是一个context的提供者
- 只有声明了这个之后,它才会作为一个context的provider来提供子树上面的context
- 如果不这么声明,即便在class里面提供了 getChildContext 这个方法,还是拿不到对应的context
-
进入
pushLegacyContextProvider
它是pushContextProvider
的别名function pushContextProvider(workInProgress: Fiber): boolean {const instance = workInProgress.stateNode;// We push the context as early as possible to ensure stack integrity.// If the instance does not exist yet, we will push null at first,// and replace it on the stack later when invalidating the context.const memoizedMergedChildContext =(instance && instance.__reactInternalMemoizedMergedChildContext) ||emptyContextObject;// Remember the parent context so we can merge with it later.// Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.previousContext = contextStackCursor.current; // 获取之前的 context 挂载到全局变量上push(contextStackCursor, memoizedMergedChildContext, workInProgress);push(didPerformWorkStackCursor,didPerformWorkStackCursor.current,workInProgress,);return true; }
以上是可以跳出当前组件的更新的一个处理情况
如果我们可以跳出组件的更新,也就是代表着当前这个 classComponent,它的state它的props都应该是没有任何变化的
这个时候, 当然是可以直接使用保存在它上面原始的 context 的对象
如果它是一个需要更新的 classComponent,需要看一下 updateClassComponent 这个更新方法
function updateClassComponent(current: Fiber | null,workInProgress: Fiber,Component: any,nextProps,renderExpirationTime: ExpirationTime,
) {// Push context providers early to prevent context stack mismatches.// During mounting we don't know the child context yet as the instance doesn't exist.// We will invalidate the child context in finishClassComponent() right after rendering.let hasContext;if (isLegacyContextProvider(Component)) {hasContext = true;pushLegacyContextProvider(workInProgress);} else {hasContext = false;}prepareToReadContext(workInProgress, renderExpirationTime);const instance = workInProgress.stateNode;let shouldUpdate;if (instance === null) {if (current !== null) {// An class component without an instance only mounts if it suspended// inside a non- concurrent tree, in an inconsistent state. We want to// tree it like a new mount, even though an empty version of it already// committed. Disconnect the alternate pointers.current.alternate = null;workInProgress.alternate = null;// Since this is conceptually a new fiber, schedule a Placement effectworkInProgress.effectTag |= Placement;}// In the initial pass we might need to construct the instance.constructClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);mountClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);shouldUpdate = true;} else if (current === null) {// In a resume, we'll already have an instance we can reuse.shouldUpdate = resumeMountClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);} else {shouldUpdate = updateClassInstance(current,workInProgress,Component,nextProps,renderExpirationTime,);}return finishClassComponent(current,workInProgress,Component,shouldUpdate,hasContext,renderExpirationTime,);
}
-
一进来就调用了
isLegacyContextProvider
方法- 就是如果它是一个contextprovider,那么它要进行 push 操作
pushLegacyContextProvider
- 就是如果它是一个contextprovider,那么它要进行 push 操作
-
接下去, 它调用了一个方法,叫做
prepareToReadContext
这么一个方法- 这个方法和新的contextAPI有关,先跳过
-
接下去基本上没有跟 context 相关的内容了,这里进入
finishClassComponent
function finishClassComponent(current: Fiber | null,workInProgress: Fiber,Component: any,shouldUpdate: boolean,hasContext: boolean,renderExpirationTime: ExpirationTime, ) {// Refs should update even if shouldComponentUpdate returns falsemarkRef(current, workInProgress);const didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;if (!shouldUpdate && !didCaptureError) {// Context providers should defer to sCU for renderingif (hasContext) {invalidateContextProvider(workInProgress, Component, false);}return bailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime,);}const instance = workInProgress.stateNode;// RerenderReactCurrentOwner.current = workInProgress;let nextChildren;if (didCaptureError &&typeof Component.getDerivedStateFromError !== 'function') {// If we captured an error, but getDerivedStateFrom catch is not defined,// unmount all the children. componentDidCatch will schedule an update to// re-render a fallback. This is temporary until we migrate everyone to// the new API.// TODO: Warn in a future release.nextChildren = null;if (enableProfilerTimer) {stopProfilerTimerIfRunning(workInProgress);}} else {if (__DEV__) {ReactCurrentFiber.setCurrentPhase('render');nextChildren = instance.render();if (debugRenderPhaseSideEffects ||(debugRenderPhaseSideEffectsForStrictMode &&workInProgress.mode & StrictMode)) {instance.render();}ReactCurrentFiber.setCurrentPhase(null);} else {nextChildren = instance.render();}}// React DevTools reads this flag.workInProgress.effectTag |= PerformedWork;if (current !== null && didCaptureError) {// If we're recovering from an error, reconcile without reusing any of// the existing children. Conceptually, the normal children and the children// that are shown on error are two different sets, so we shouldn't reuse// normal children even if their identities match.forceUnmountCurrentAndReconcile(current,workInProgress,nextChildren,renderExpirationTime,);} else {reconcileChildren(current,workInProgress,nextChildren,renderExpirationTime,);}// Memoize state using the values we just used to render.// TODO: Restructure so we never read values from the instance.workInProgress.memoizedState = instance.state;// The context might have changed so we need to recalculate it.if (hasContext) {invalidateContextProvider(workInProgress, Component, true);}return workInProgress.child; }
hasContext
是否是一个 contextProvider- 如果是 true, 则执行
invalidateContextProvider(workInProgress, Component, false);
function invalidateContextProvider(workInProgress: Fiber,type: any,didChange: boolean, ): void {const instance = workInProgress.stateNode;invariant(instance,'Expected to have an instance by this point. ' +'This error is likely caused by a bug in React. Please file an issue.',);// 如果有变化if (didChange) {// Merge parent and own context.// Skip this if we're not updating due to sCU.// This avoids unnecessarily recomputing memoized values.const mergedContext = processChildContext(workInProgress,type,previousContext,);instance.__reactInternalMemoizedMergedChildContext = mergedContext;// Replace the old (or empty) context with the new one.// It is important to unwind the context in the reverse order.pop(didPerformWorkStackCursor, workInProgress);pop(contextStackCursor, workInProgress);// Now push the new context and mark that it has changed.push(contextStackCursor, mergedContext, workInProgress);push(didPerformWorkStackCursor, didChange, workInProgress);} else {pop(didPerformWorkStackCursor, workInProgress);push(didPerformWorkStackCursor, didChange, workInProgress);} }
- 通过
processChildContext
计算出新的 context 并挂载到__reactInternalMemoizedMergedChildContext
- 之后,pop 2次,push 2次 来处理了栈内的顺序
- 进入
processChildContext
看下这个方法function processChildContext(fiber: Fiber,type: any,parentContext: Object, ): Object {const instance = fiber.stateNode;const childContextTypes = type.childContextTypes;// TODO (bvaughn) Replace this behavior with an invariant() in the future.// It has only been added in Fiber to match the (unintentional) behavior in Stack.// 这个属性一定是 function 才能生效if (typeof instance.getChildContext !== 'function') {if (__DEV__) {const componentName = getComponentName(type) || 'Unknown';if (!warnedAboutMissingGetChildContext[componentName]) {warnedAboutMissingGetChildContext[componentName] = true;warningWithoutStack(false,'%s.childContextTypes is specified but there is no getChildContext() method ' +'on the instance. You can either define getChildContext() on %s or remove ' +'childContextTypes from it.',componentName,componentName,);}}return parentContext;}let childContext;if (__DEV__) {ReactCurrentFiber.setCurrentPhase('getChildContext');}startPhaseTimer(fiber, 'getChildContext');childContext = instance.getChildContext(); // 执行这个 提供的api, 获取数据stopPhaseTimer();if (__DEV__) {ReactCurrentFiber.setCurrentPhase(null);}for (let contextKey in childContext) {invariant(contextKey in childContextTypes,'%s.getChildContext(): key "%s" is not defined in childContextTypes.',getComponentName(type) || 'Unknown',contextKey,);}// 忽略if (__DEV__) {const name = getComponentName(type) || 'Unknown';checkPropTypes(childContextTypes,childContext,'child context',name,// In practice, there is one case in which we won't get a stack. It's when// somebody calls unstable_renderSubtreeIntoContainer() and we process// context from the parent component instance. The stack will be missing// because it's outside of the reconciliation, and so the pointer has not// been set. This is rare and doesn't matter. We'll also remove that API.ReactCurrentFiber.getCurrentFiberStackInDev,);}// 最终是两者 mergereturn {...parentContext, ...childContext}; }
- 其实,这个 processChildContext 非常简单,获取 context,合并 context
- 这里的 parentContext 是传入的 previousContext, 这个是上面调用 pushContextProvider 时设置的全局变量
contextStackCursor.current
- 也就是 父组件中 提供的 context 对象,最终都是为了合并
- 通过
-
总结来说,父子孙三个组件,在更新子组件的时候,先去push了一个它之前存在的这个属性
-
因为我们不知道这个组件它是否要更新,不管它是否要更新,都要先都要执行 push 的一个操作
-
所以,先 push 一个老的值进去再说, 然后到后面,如果发现这个组件它是要更新的,就调用这个
invalidateContextProvider
方法 -
调用了这个方法之后, 根据传进来的
didChange
,如果是 true 表示要更新,要重新去计算一个新的合并过的这个context, 即mergedContext
给它推入到栈里面 -
对于子组件来说,它的所有子树所获取到的context肯定是经过子组件,和上层的父组件合并的 context 了, 也就是
contextStackCursor
这里 -
同时对于
didPerformWorkStackCursor
来说,因为didChange
是 true,它的 current 肯定也是 true -
如果
didChange
是 false,这个时候不需要改变contextStackCursor
- 因为 push 的本来就是上面的那个值,也就是上一次计算出来的这个值
- 就是保存在
__reactInternalMemoizedMergedChildContext
这上面的值 - 因为它本身没有变化,不需要去改动它,而对于
didPerformWorkStackCursor
来说,需要去改变它 - didChange 变成false,我没有更新,不能继续存之前的那个值, 因为之前的那个值可能是更新过了,它可能是 true
- 我这次发现这个组件是不需要更新的, 要把它改成 false
- 在一开始,这个方法就是叫做 hasContextChaned 的这个方法,它是用来判断这个组件
- 是否可以跳过更新的一个非常关键的一个值, 如果它返回的一直是 true
- 会导致我们每一个组件都需要去更新,而最终导致整个性能变得非常的差
- 所以这就是
didPerformWorkStackCursor
它的一个作用
-
老的 context api当中的 push 操作是比较复杂的,要进行一个 context 的合并这么一个过程
-
到这里为止,将context的合并,并让它入栈
-
注意,还有一种情况是这样的,父子孙三层组件,有一个子组件没有儿子组件
-
也就是有多个子组件,其中有的子组件没有下层组件,这时候这类子组件拿到的是父组件原来的,而非合并过的
-
看下具体的代码处理, 比如说我们随便挑一个 class component,更新过程当中调用的方法, 如
updateClassInstance
这个方法// Invokes the update life-cycles and returns false if it shouldn't rerender. function updateClassInstance(current: Fiber,workInProgress: Fiber,ctor: any,newProps: any,renderExpirationTime: ExpirationTime, ): boolean {const instance = workInProgress.stateNode;// ... 跳过很多代码const contextType = ctor.contextType; // 注意这里let nextContext;if (typeof contextType === 'object' && contextType !== null) {nextContext = readContext(contextType);} else {// 注意这个 else, 这个是重点const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);}// ... 跳过很多代码return shouldUpdate; }
-
这里有一个在
ctor.contextType
在 classComponent 上面去读取这个属性 -
注意,这里
contextType
和contextTypes
的区别- 后者在应用中,我们自己的代码里用于设定接收上层属性的
- 前者就是要去读取某一个新的 contextAPI 它的 provider 上面提供的属性
- 也就是说可以这么设置
Child.contextType = Consumer
- 两者共同存在,则后者老的失效
- 也就是说,如果 孙组件使用了 新的 contextType 这个api, 后面同时定义的 contextTypes 相关的会失效
-
对于react来说,它即将把所有context相关的东西呢都放在新的contextAPI里面
- 所以如果我们优先使用了 contextType 这种新的 context API 的使用方式
- 就直接默认只使用新的 context
- 而只有在没有使用这个新的 context API 的时候才会去使用老的 context API
- 这个时候我们就会去调用一个叫做
getUnmaskedContext(workInProgress, ctor, true)
- 这个方法来去读取用在这一个组件上面它所对应的context属性
function getUnmaskedContext(workInProgress: Fiber,Component: Function,didPushOwnContextIfProvider: boolean, ): Object {if (didPushOwnContextIfProvider && isContextProvider(Component)) {// If the fiber is a context provider itself, when we read its context// we may have already pushed its own child context on the stack. A context// provider should not "see" its own child context. Therefore we read the// previous (parent) context instead for a context provider.return previousContext;}return contextStackCursor.current; }
didPushOwnContextIfProvider
传进来的时候是 true- 代表是否已经 push 了自己的 contextProvider,对于我们自己是一个 contextProvider 的一个情况
- 在调用update之前,那么肯定是已经调用过push了, 如果提供了 context,这个值就是true
- 而后面还需要判断它是否是一个 contextProvider,如果这两个条件都符合,返回的是 previousContext
- 这个 previousContext 就是之前在调用 push 操作的时候,即
pushContextProvider
,给它赋值的这个值 - 就比如说在更新 子组件 的过程中,执行 updateClassComponent
- 先 push 了自己的 contextProvider, 这个时候赋值给了 previousContext
- 它等于 push 之前的那个context,就是 父组件提供的 context
- 对于 子组件 更新的过程当中使用的 context,肯定不能使用自己提供的这个 value
- 子组件提供的value是给自己子树,也就是孙组件及之后使用的
- 子组件要用context的话,应该是去读取父组件提供的 context
- 子组件要去获取父组件提供的context,要去调用
getUnmaskedContext
这个方法 - 而如果组件同时是一个provider,那它肯定已经push过了,所以组件必须返回 previousContext 才行
- 同样,如果组件不是一个 contextprovider,当然没有执行过push,只需要执行当前这个cursor即可
- 进入
getMaskedContext
function getMaskedContext(workInProgress: Fiber,unmaskedContext: Object, ): Object {const type = workInProgress.type;const contextTypes = type.contextTypes;if (!contextTypes) {return emptyContextObject;}// Avoid recreating masked context unless unmasked context has changed.// Failing to do this will result in unnecessary calls to componentWillReceiveProps.// This may trigger infinite loops if componentWillReceiveProps calls setState.const instance = workInProgress.stateNode;if (instance &&instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) {return instance.__reactInternalMemoizedMaskedChildContext;}// 注意,这里是核心const context = {};for (let key in contextTypes) {context[key] = unmaskedContext[key];}if (__DEV__) {const name = getComponentName(type) || 'Unknown';checkPropTypes(contextTypes,context,'context',name,ReactCurrentFiber.getCurrentFiberStackInDev,);}// Cache unmasked context so we can avoid recreating masked context unless necessary.// Context is created before the class component is instantiated so check for instance.if (instance) {cacheContext(workInProgress, unmaskedContext, context);}return context; }
- 这个方法是在组件的父组件中所有合并过的 context 中获得当前组件需要读取的属性
- 也就是说当前组件需要什么指定什么,context中就返回什么
-
以上,就是对于一个 Class Component,要使用老的contextAPI, 如何去提供这个context以及它如何去获取这个context的一个过程
-
以上是 push 操作,那什么时候才会 pop 呢?
-
在
completeUnitOfWork
的时候,在 packages/react-reconciler/src/ReactFiberCompleteWork.js#L540function completeWork(current: Fiber | null,workInProgress: Fiber,renderExpirationTime: ExpirationTime, ): Fiber | null {const newProps = workInProgress.pendingProps;// ... 跳过很多代码switch (workInProgress.tag) {// ... 跳过很多代码case ClassComponent: {const Component = workInProgress.type;if (isLegacyContextProvider(Component)) {popLegacyContext(workInProgress);}break;}// ... 跳过很多代码}// ... 跳过很多代码 }
- 对于 classComponent,如果它是一个 provider,它必须要
popLegacyContext
别名是popContext
// packages/react-reconciler/src/ReactFiberContext.js#L124 function popContext(fiber: Fiber): void {pop(didPerformWorkStackCursor, fiber);pop(contextStackCursor, fiber); }
- 就把两个 cursor 给它 pop 一下
- 在这里回到之前的push进行一下对比
// packages/react-reconciler/src/ReactFiberContext.js#L215 function pushContextProvider(workInProgress: Fiber): boolean {// ... 跳过很多代码push(contextStackCursor, memoizedMergedChildContext, workInProgress);push(didPerformWorkStackCursor,didPerformWorkStackCursor.current,workInProgress,);return true; }
- 这里先push的是
contextStackCursor
再push的是didPerformWorkStackCursor
- 这里先push的是
- 回到pop, 先pop的是
didPerformWorkStackCursor
再pop的是contextStackCursor
- 这就是我之前说过的,push 是按哪个顺序, pop的时候,必须要反过来去做
- 这样的话,在 context-stack 中的 valueStack 里面存储的值对应的cursor的位置才是能真正对应起来
- 对于在 completeWork 里面,我们只需要去执行
popLegacyContext
这个操作就可以了
- 对于 classComponent,如果它是一个 provider,它必须要
-
还是用之前的 Fiber 树来举例子

- 假设整个树形结构下面的每一个节点,它都是一个classcomponent
- 这个时候我们更新,然后 App 和 这个 div,它都提供了
childContextTypes
- 这个时候对于 input 这个 classcomponent,它要更新的过程中肯定要获取
- 这个App和和div提供的context合并之后的那个对象,对App更新的过程中,它调用了push
- 然后在div执行更新的时候,它先去获取App push 的那个 context 作为它更新的时候要用的那个context
- 它自己也要去push一个context,push的时候要跟App提供的context进行一个合并
- 这个时候游标已经到了第二个context了, 第一个context是App提供的, 第二个是App和和div合并的
- 这两个值都是在stack栈里面有存着的,只不过现在的
contextStackCursor
这个游标 - 指向的是div它合并过之后的那个context,这个context是提供给Input渲染更新的时候,它要获取的那个值
- 就这么一层一层下来之后,到最后更新 input 这个节点的时候,它拿到的context是上面这三个组件,它合并过的 context
- 对于List的更新,它不需要Input合并进去,它只需要App和div它们合并之后的那个 context 对象
- 这个就是我们在 completeUnitOfWork 的时候,要去pop这个stack
- 在input节点执行 completeUnitOfWork 的时候
- 要 pop input节点及以上提供的context合并之后的一个值
- 对应 cursor 指向 App, div, Input 合并后的 context
- 在Input节点执行 completeUnitOfWork 的时候
- 要 pop Input节点及以上提供的context合并之后的一个值
- 对应 cursor 指向 App 和 div 合并后的 context
- 在Input发现sibling节点 List,要对这个兄弟节点执行 beginWork,也就是更新的流程
- 这个时候 List 拿到的这个 context,也就是 App 和 div合并之后的节点
- 所以这就是在 beginWork 的时候,要对 classcomponent 进行一个push的操作
- 等到要去 completeUnitOfWork 的时候,执行到每个节点,要执行对应的pop
- 所以这个时候就可以对节点 push 和 pop 的位置可以一一对应起来
- 这样的话就不会造成整个stack里面的这个顺序混淆
- 在input节点执行 completeUnitOfWork 的时候
- 这就是对于 childContextTypes 这种context提供的方式的一个使用的过程
- 这个 API 最终被删除了,因为前面谈到的性能问题,以上是它的整体原理
相关文章:

React16源码: React中处理LegacyContext相关的源码实现
LegacyContext 老的 contextAPI 也就是我们使用 childContextTypes 这种声明方式来从父节点为它的子树提供 context 内容的这么一种方式遗留的contextAPI 在 react 17 被彻底移除了,就无法使用了那么为什么要彻底移除这个contextAPI的使用方式呢?因为它…...

Boost.Test资源及示例
Note:boost_1_84_0的动态连接库资源链接 1.代码组织如下图: 2.包括程序入口的代码文件 示例: // M24.01.MyTestModule.cpp : 定义控制台应用程序的入口点。 //#include "stdafx.h" #define BOOST_TEST_MODULE MYTESTMODULE #def…...
数据结构二叉树
二叉树是数据结构中的一个基本概念,它是每个节点最多有两个子节点的树结构。在二叉树中,每个节点通常有两个指针,分别指向左子节点和右子节点。 数据结构定义 在二叉树的节点中,通常包含以下信息: 数据域࿱…...
JavaScript继承与原型链
继承和原型链是什么? 1.1 在继承中,子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法。这意味着子类可以使用父类的方法和属性,使用继承的目的是为了更好设置实例的公共属性和方法,如下例子: …...
SouthLeetCode-打卡24年01月第4周
SouthLeetCode-打卡24年01月第4周 // Date : 2024/01/22 ~ 2024/01/28 022.设计链表 - 双链表 (1) 题目描述 022#LeetCode.707.#北岸计划2024/01/22 (2) 题解代码 import java.util.List;class ListNode {int val;ListNode prev;ListNode next;ListNode(){this.val 0;th…...

Linux——磁盘和文件系统(一)
Linux——磁盘和文件系统 磁盘机械式磁盘固态硬盘 机械式磁盘结构磁盘,磁道,扇区柱面 文件系统的初始化划卷(划盘) 挂载C盘放了什么东西Boot Block(启动模块) 0号组放了什么东西Super Block(超级…...

EasyCVR视频智能监管系统方案设计与应用
随着科技的发展,视频监控平台在各个领域的应用越来越广泛。然而,当前的视频监控平台仍存在一些问题,如视频质量不高、监控范围有限、智能化程度不够等。这些问题不仅影响了监控效果,也制约了视频监控平台的发展。 为了解决这些问…...

Ubuntu搭建国标平台wvp-GB28181-pro
目录 简介安装和编译1.查看操作系统信息2.安装最新版的nodejs3.安装java环境4.安装mysql5.安装redis6.安装编译器7.安装cmake8.安装依赖库9.编译ZLMediaKit9.1.编译结果说明 10.编译wvp-GB28181-pro10.1.编译结果说明 配置1.WVP-PRO配置文件1.1.Mysql数据库配置1.2.REDIS数据库…...
LC 2808. 使循环数组所有元素相等的最少秒数
2808. 使循环数组所有元素相等的最少秒数 难度: 中等 题目大意: 给你一个下标从 0 开始长度为 n 的数组 nums 。 每一秒,你可以对数组执行以下操作: 对于范围在 [0, n - 1] 内的每一个下标 i ,将 nums[i] 替换成 nums[i] &…...

Qt|大小端数据转换
后面打算写Qt关于网络编程的博客,网络编程就绕不开字节流数据传输,字节流数据的传输一般是根据协议来定义对应的报文该如何组包,那这就必然牵扯到了大端字节序和小端字节序的问题了。不清楚的大小端的可以看一下相关资料:大小端模…...

禅道添加自定义字段
1,数据库表 zt_story 添加自定义字段 bakDate1,bakDate2,bakDate3,bakDate4 2,在 /opt/lampp/htdocs/zentaopms/extension/custom/story/ext/config 中添加bakDate.php文件 <?php $config->story->datatab…...

蓝桥杯2024/1/26笔记-----基于PCF8591的电压采集装置
功能实现要求: 每次建好工程文件夹,里边包含User(放工程文件,mian.c,可以在这里写如同我这个文章的文本文档)、Driver(存放底层文件如Led.c,Led.h等) 新建的工程先搭建框…...

【一】esp32芯片开发板环境搭建
1、esp32的源码在github上的地址 不同的芯片支持的源码版本不一样,需要根据自己的实际的esp32开发板的芯片下载不用版本的代码 esp32支持多种开发方式,如arduino,ESP-IDF等。官方推荐使用idf开发,ESP-IDF 是乐鑫官方推出的物联网开…...

PyTorch2ONNX-分类模型:速度比较(固定维度、动态维度)、精度比较
图像分类模型部署: PyTorch -> ONNX 1. 模型部署介绍 1.1 人工智能开发部署全流程 #mermaid-svg-bAJun9u4XeSykIbg {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-bAJun9u4XeSykIbg .error-icon{fill:#552222;}…...
Docker命令快车道:一票通往高效开发之旅
欢迎登上 Docker 命令快车!在这趟旅程中,你不仅会学会如何驾驭 Docker 这辆神奇的车,还会发现如何让你的开发旅程变得更加轻松愉快。现在,请系好安全带,我们即将出发! Docker 是什么 Docker 就像是一辆超…...
IP类接口大全,含免费次数
IP查询 IP归属地-IPv4高精版:根据IP地址查询归属地信息,支持到中国地区(不含港台地区)街道级别,包含国家、省、市、区县、详细地址和运营商等信息。IP归属地-IPv4区县级:根据IP地址查询归属地信息…...
LLMs 的记忆和信息检索服务器 Motorhead
LLMs 的记忆和信息检索服务器 Motorhead 1. 为什么使用 Motorhead?2. 通过 Docker 启动 Motorhead3. Github 地址4. python 使用示例地址 1. 为什么使用 Motorhead? 使用 LLMs构建聊天应用程序时,每次都必须构建记忆处理。Motorhead是协助该…...

vue3项目中让echarts适应div的大小变化,跟随div的大小改变图表大小
目录如下 我的项目环境如下利用element-resize-detector插件监听元素大小变化element-resize-detector插件的用法完整代码如下:结果如下 在做项目的时候,经常会使用到echarts,特别是在做一些大屏项目的时候。有时候我们是需要根据div的大小改…...
springboot启动异常
Error creating bean with name ‘dataSource’ org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name dataSource: Unsatisfied dependency expressed through field basicProperties; nested exception is org.springframew…...

直播主播之互动率与促单
直播互动率是衡量直播间观众参与度的重要指标,通常指的是直播间的观众点赞、评论以及转发的数量。互动率越高,表明观众参与度越高,直播间的人气值也相应越高。 为了提升直播互动率,主播可以采取以下策略: 1.积极引导观众参与互动…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

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

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

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

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...