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
  • 接下去, 它调用了一个方法,叫做 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 上面去读取这个属性

  • 注意,这里 contextTypecontextTypes 的区别

    • 后者在应用中,我们自己的代码里用于设定接收上层属性的
    • 前者就是要去读取某一个新的 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#L540

    function 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
    • 回到pop, 先pop的是 didPerformWorkStackCursor 再pop的是 contextStackCursor
    • 这就是我之前说过的,push 是按哪个顺序, pop的时候,必须要反过来去做
    • 这样的话,在 context-stack 中的 valueStack 里面存储的值对应的cursor的位置才是能真正对应起来
    • 对于在 completeWork 里面,我们只需要去执行 popLegacyContext 这个操作就可以了
  • 还是用之前的 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里面的这个顺序混淆
  • 这就是对于 childContextTypes 这种context提供的方式的一个使用的过程
  • 这个 API 最终被删除了,因为前面谈到的性能问题,以上是它的整体原理

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

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

相关文章

知识点积累系列(一)golang语言篇【持续更新】

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 知识点积累 系列文章的第一篇&#xff0c;记录golang语言相关的知识点 1.结构体的mapstructure是什么 mapstructure:"default" mapstructure是一个Go语言的库&#xff0c;用于将一个map中的值映射到…

C语言王道第八周一题

Description 初始化顺序表&#xff08;顺序表中元素为整型&#xff09;&#xff0c;里边的元素是 1,2,3&#xff0c;然后通过 scanf 读取一个元素&#xff08;假如插入的是 6&#xff09;&#xff0c;插入到第 2 个位置&#xff0c;打印输出顺序表&#xff0c;每个 元素占 3 个…

添加了gateway之后远程调用失败

前端提示500&#xff0c;后端提示[400 ] during [GET] to [http://userservice/user/1] 原因是这个&#xff0c;因为在请求地址写了两个参数&#xff0c;实际上只传了一个参数 解决方案&#xff1a;加上(required false)并重启所有相关服务

【程序员英语】【美语从头学】初级篇(入门)(笔记)Lesson13(买东西)(餐厅点餐事宜;询问有无座位;食物如何调理:牛排、咖啡等;菜单等相关)

《美语从头学初级入门篇》 注意&#xff1a;被 删除线 划掉的不一定不正确&#xff0c;只是不是标准答案。 文章目录 Lesson 13 At the Restaurant 在餐厅会话A会话B笔记餐厅询问有无座位&#xff1b;餐厅电话订座其他餐厅询问有无座位的问法 吸烟区与非吸烟区&#xff08;smo…

[力扣 Hot100]Day18 矩阵置零

题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 出处 思路 在原数组上直接操作势必会出现“冗余”的0&#xff0c;即原本[i,j]处不是0&#xff0c;例如由于i行的其他位置有0导致[i,j]…

Shell中的AWK

1.awk的工作原理 逐行读取文本&#xff0c;默认以空格或tab键为分隔符进行分隔&#xff0c;将分隔所得的各个字段保存到内建变量中&#xff0c;并按模式或者条件执行编辑命令。awk倾向于将一行分成多个"字段"然后再进行处理。awk信息的读入也是逐行读取的&#xff0…

day38_MySQL

今日内容 0 复习昨日 1 引言 2 数据库 3 数据库管理系统 4 MySQL 5 SQL语言 0 复习昨日 1 引言 1.1 现有的数据存储方式有哪些&#xff1f; Java程序存储数据&#xff08;变量、对象、数组、集合&#xff09;&#xff0c;数据保存在内存中&#xff0c;属于瞬时状态存储。文件&…

删除有序数组中的重复项[简单]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给你一个非严格递增排列的数组nums&#xff0c;请你原地删除重复出现的元素&#xff0c;使每个元素只出现一次&#xff0c;返回删除后数组的新长度。元素的相对顺序应该保持一致。然后返回nums中唯一元素的个数。 考虑nums的唯一元素…

Source Insight 4的使用经验

问题1 函数结束提示符<<end tagDebugData>> 在source insight里的options→preference→syntax decorations→auto annotations→annotate closing braceswith end-comments关闭或启动显示. 但是有的有有的没有&#xff0c;奇怪! 参照网络答案&#xff1a; Sou…

DataX介绍

一、介绍 DataX 是一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。 github地址 详细文档 操作手册 支持数据框架如下&#xff1a; 架构 Reader&#xff1…

IPV4 转 IPV6 解决方案大全

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

App全测试扫描漏洞工具

APP 有漏洞被测要下架&#xff0c;怎么处理&#xff1f; 如题&#xff0c;今天被问到&#xff1a;市面上有什么好的 APP 漏洞扫描工具推荐&#xff1f;我们的 APP 有漏洞&#xff0c;需要下架 APP&#xff1f; 前言 事情的经过是这样的&#xff1a; 1&#xff1a;学员公司测试…

UnityShader(九)Unity中的基础光照(下)

标准光照模型 光照模型有许多种&#xff0c;但在早期游戏引擎中&#xff0c;往往只使用一个光照模型&#xff0c;被称为标准光照模型。 标准光照模型只关心直接光照&#xff0c;也就是那些直接从光源发射出来照射到物体表面后&#xff0c;经过物体表面的一次反射直接进入摄像…

在Meteor Lake上测试基于Stable Diffusion的AI应用

上个月刚刚推出的英特尔新一代Meteor Lake CPU&#xff0c;预示着AI PC的新时代到来。AI PC可以不依赖服务器直接在PC端处理AI推理工作负载&#xff0c;例如生成图像或转录音频。这些芯片的正式名称为Intel Core Ultra处理器&#xff0c;是首款配备专门用于处理人工智能任务的 …

【安装记录】在pve中创建debian12虚拟机

1、官网下载debian12:https://www.debian.org/ 2、安装虚拟机 &#xff08;1&#xff09;上传 iso 文件到pve服务器&#xff1a; 等待上传中。。。 上传完毕结果如下&#xff1a; &#xff08;2&#xff09;创建虚拟机 类别我也不清楚有什么区别&#xff1f;我看推荐使用host…

[数据结构与算法]哈希算法

目录 哈希算法 常见哈希函数&#xff1a; MD5 (Message Digest Algorithm 5): SHA-1 (Secure Hash Algorithm 1): SHA-256 (Secure Hash Algorithm 256-bit): 代码演示: 哈希算法 哈希算法是一种将任意长度的输入数据映射为固定长度的输出数据的算法。哈希函数的主要目标是…

瑞_数据结构与算法_红黑树

文章目录 1 什么是红黑树1.1 红黑树的背景1.2 红黑树的特性 ★★★ 2 红黑树的Java实现2.1 红黑树颜色枚举类Color2.2 红黑树节点类Node2.2.1 实现判断是否是左孩子方法isLeftChild()2.2.2 实现查找叔叔节点方法uncle()2.2.3 实现查找兄弟节点方法sibling() 2.3 红黑树类RedBla…

Python如何递归删除空文件夹

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 1.Python如何递归删除空文件夹&#xff0c;这个问题很常见。 但大多数人的解决办法都是自己实现递归函数解决这个问题&#xff0c;其实根本不用那么麻烦。 Python中的os.walk提供了一种从内到外的遍历目录树的方法&#xff…

fullcalendar案例

fullcalendar案例 <script srchttps://cdn.jsdelivr.net/npm/fullcalendar6.1.10/index.global.min.js></script><script srchttps://code.jquery.com/jquery-3.6.0.min.js></script> <!-- 引入 jQuery CDN --><script>document.addEventL…

第5章 python深度学习——波斯美女

第5章 深度学习用于计算机视觉 本章包括以下内容&#xff1a; 理解卷积神经网络&#xff08;convnet&#xff09; 使用数据增强来降低过拟合 使用预训练的卷积神经网络进行特征提取 微调预训练的卷积神经网络 将卷积神经网络学到的内容及其如何做出分类决策可视化 本章将…