【React Hooks原理 - createContext、useContext】

概述

在前面React Hooks系列介绍中我们知道React为了更好的处理不同生命周期的特殊处理,除了useContext这个Hooks之外的其他Hooks都拆为了Mount、Update两个阶段,而useContext内部并没有区分都是通过调用readContext来获取上下文的,下面就来介绍一下useContext为什么不拆分以及其内部实现原理?

基本概念

先回答上下面的问题: 为什么useContext不和其他Hooks一样拆分为Mount、Update阶段处理呢?

  • 这是因为React之所以拆分是针对复杂的状态和副作用管理这样可以更新清晰的处理不同生命周期的处理,比如Mount主要关注初始化挂载,Update主要进行更新渲染,而useContext只是订阅并获取上下文的值,不存在复杂的状态和副作用,所以不需要拆分过程。

useContext可以帮助我们跨越组件层级直接传递变量,避免了在每一个层级手动的传递 props 属性,进而实现状态共享。它的工作就是订阅上下文,并当该上下文变化时,触发组件更新重新渲染,所以这个Hook依赖createContext这个API来先创建一个上下文。本文也会从createContext入手先介绍createContext自带的Provider、Consumer然后介绍useContext。

基本使用

createContext

我们知道createContext接收一个默认值,会返回一个包含Provider、Consumer的上下面,其中Provider作为上下文数据的提供者,Consumer作为数据的消费者,以一个接收上下文数据并返回一段JSX的函数进行消费。

PS:Consumer只能获取上级Provider提供的数据,同级或者子级的无法获取。useContext和Consumer使用的是同一个算法去查找上下文,所以也是无法获取同级/子级别数据

createContext接收的默认值永远不会变化,只是一个异常的兜底策略,当通过Comsumer消费时会从上级组件逐渐往上查找,直到找到最近的Provider提供者,并获取其提供的值,如果到root根节点都没有找到则会使用该默认值。

Demo实例:

// App.js
const SomeContext = createContext(defaultValue)
function App() {const [theme, setTheme] = useState('light');// ……return (<ThemeContext.Provider value={theme}><Page /></ThemeContext.Provider>);
}// Page.js
function Page() {// 遗留方式 (不推荐)return (<ThemeContext.Consumer>{theme => (<button className={theme} />)}</ThemeContext.Consumer>);
}

正如React官网所说目前不推荐使用Consumer获取上下文的方式,但是useContext是一个Hook只能在函数式组件或者自定义Hook中使用,所以在类组件中常使用该方式获取。虽然目前主流使用函数式组件,但是一些较老的项目中仍然使用类组件方式,所以这里简单介绍下。

useContext

useContext是React推荐的在函数式组件中获取上下文信息的一个Hook,其只能在函数组件顶层以及自定义Hook中使用。该Hook简化了获取上下文的过程,并解决了使用Consumer函数式返回的使用成本,将数据直接作为对象返回,使代码更简洁和清晰。

// Page.js
function Page() {// ✅ 推荐方式const theme = useContext(ThemeContext);return <button className={theme} />;
}

源码解析

经过上面介绍,对其使用已经有了初步了解,这里就直接上代码。

createContext

createContext定义:

export function createContext<T>(defaultValue: T): ReactContext<T> {const context: ReactContext<T> = {// flag标识是一个symbol常量// export const REACT_CONTEXT_TYPE: symbol = Symbol.for('react.context');$$typeof: REACT_CONTEXT_TYPE, _currentValue: defaultValue, // 用于主渲染器 React-dom(web端)、react-native(移动端)_currentValue2: defaultValue, // 用于次渲染器 React ART(web端)和React Fabric(移动端)。_threadCount: 0, // _threadCount 用于追踪当前有多少个并发任务在使用该上下文,便于更好的状态管理// These are circularProvider: (null: any), // 上下文数据提供 value = anyConsumer: (null: any), // 获取上下文信息 children = (value) = ReactElement};// enableRenderableContext用于控制上下文对象的 Provider 和 Consumer 的设置方式。一般为true,所以这里截取true的代码(context.Provider = context;context.Consumer = {$$typeof: REACT_CONSUMER_TYPE,_context: context,};return context;
}

从代码能看出,createContext接收一个默认值defaultValue,然后会返回一个上下文context,其本质就是一个带有特殊属性的对象,其中会设置context.Provider指向context本身,而Consumer指向带有$$typeof和context的对象。在这里虽然Provider指向context本身,其$$typeof是REACT_CONTEXT_TYPE,但是React内部也会按照REACT_PROVIDER_TYPE来处理Provider

这里需要注意$$typeof属性,因为这个字段让React知道当前对象是什么,比如这里context是一个对象,但是我们可以通过组件的方式使用<Context.Provider />,就是因为React根据$$typeof判断将Provider、Consumer渲染为了一个组件fiber节点。

在协调阶段React会根据JSX代码将其转换为Vdom然后转换为fiber节点并进行对比,最后生成需要更新的fiber树WorkInProgress Tree,然后通知渲染器渲染。流程可以理解为: JSX -> Vdom Tree -> Fiber Tree -> WorkInProgress Tree -> Renderer

Provider

从上面知道在使用createContext时返回带有$$typeof的context对象后,我们可以在组件内通过<Context.Provide value={state}>组件的方式来使用Provider,现在来看看其内部是如何实现的。

我们知道所有节点会在协调阶段会进行fiber构造循环通过createFiberFromElement函数将虚拟dom转换为fiber节点进而生成fiber树,在fiber构造时会进入到beginWork进行fiber节点的处理,在该函数中会对Provider类型的节点进行处理:

在执行createFiberFromElement函数时会调用createFiberFromTypeAndProps其中会将Provider打上ContextProvider的fiberTag

if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {case REACT_PROVIDER_TYPE:if (!enableRenderableContext) {fiberTag = ContextProvider;break getTag;}// Fall throughcase REACT_CONTEXT_TYPE:if (enableRenderableContext) {fiberTag = ContextProvider;break getTag;} else {fiberTag = ContextConsumer;break getTag;}case REACT_CONSUMER_TYPE:if (enableRenderableContext) {fiberTag = ContextConsumer;break getTag;}

由此可见React会将REACT_PROVIDER_TYPEREACT_CONTEXT_TYPE类型都处理为ContextProvider组件,这就解释了上面createContext时Provider的$$typeof为什么为REACT_CONTEXT_TYPE却能使用Provider组件的原因。

function beginWork(current: Fiber | null,workInProgress: Fiber,renderLanes: Lanes): Fiber | null {switch (workInProgress.tag) {case ContextProvider:return updateContextProvider(current, workInProgress, renderLanes);case ContextConsumer:return updateContextConsumer(current, workInProgress, renderLanes);default: ;
}

在处理ContextProvider的tag时会调用updateContextProvider函数进行以下处理:

  • 通过pushProvider将Provider提供的新的上下文保存在context.current中

  • 通过Object.is判断上下文是否变化

    • 如果没有上次遗留的变化以及子节点改变则调用bailoutOnAlreadyFinishedWork跳过本次更新
    • 如果上下文变化则调用propagateContextChange通过所以订阅该上下文的子级更新
function updateContextProvider(current: Fiber | null,workInProgress: Fiber,renderLanes: Lanes
) {const newProps = workInProgress.pendingProps;const oldProps = workInProgress.memoizedProps;const newValue = newProps.value;// 将newValue保存在context.current中pushProvider(workInProgress, context, newValue);if (oldProps !== null) {const oldValue = oldProps.value;if (is(oldValue, newValue)) {if (// 检查 children 是否相同以及是否有遗留上下文变化。oldProps.children === newProps.children &&!hasLegacyContextChanged()) {// 跳过本次更新return bailoutOnAlreadyFinishedWork(current,workInProgress,renderLanes);}} else {// 上下文改变,该函数会找到所有使用了这个上下文的消费者,并标记这些消费者需要更新。propagateContextChange(workInProgress, context, renderLanes);}}
}function pushProvider<T>(providerFiber: Fiber,context: ReactContext<T>,nextValue: T,): void {// 将context._currentValue 保存在valueCursor.current上push(valueCursor, context._currentValue, providerFiber);// 更新context._currentValue为最新的上下文context._currentValue = nextValue;
}// 保存上下文的栈,先保存旧的上下文等pop的时候可以恢复,然后更新context.current为最新的上下文
function push<T>(cursor: StackCursor<T>, value: T, fiber: Fiber): void {index++;valueStack[index] = cursor.current;  cursor.current = value;
}

propagateContextChange函数主要就是调用propagateContextChange_eager使用类似DFS算法来查找所有订阅该上下文的组件并通知其进行更新。

// 使用DFS深度优先遍历查找所有消费上下文的组件并添加需要更新的标记,类组件会创建强制刷新forceUpdate并添加到更新队列中
function propagateContextChange_eager<T>(workInProgress: Fiber,context: ReactContext<T>,renderLanes: Lanes
): void {// DFS遍历当前节点while (fiber !== null) {let nextFiber;// 使用useContext/Consumer消费时在首次挂载时候就会创建一个依赖列表并将其所依赖的上下文保存在fiber.dependency中const list = fiber.dependencies;if (list !== null) {nextFiber = fiber.child;let dependency = list.firstContext;while (dependency !== null) {// 判断上下文是否变化if (dependency.context === context) {// 对类组件更新更新if (fiber.tag === ClassComponent) {// 创建一个强制更新并添加到更新队列中const lane = pickArbitraryLane(renderLanes);const update = createUpdate(lane);update.tag = ForceUpdate;const updateQueue = fiber.updateQueue;if (updateQueue === null) {// Only occurs if the fiber has been unmounted.} else {const sharedQueue: SharedQueue<any> = (updateQueue: any).shared;const pending = sharedQueue.pending;if (pending === null) {// 首次挂载,创建一个循环链表update.next = update;} else {update.next = pending.next;pending.next = update;}sharedQueue.pending = update;}}// 向上遍历: 从consumer节点开始, 向上遍历, 修改父路径上所有节点的fiber.childLanes属性, 表明其子节点有改动, 子节点会进入更新逻辑.scheduleContextWorkOnParentPath(fiber.return,renderLanes,workInProgress,);...break;}dependency = dependency.next;}} else if (fiber.tag === ContextProvider) {// 性能优化,跳过Provider提供者,避免不必要的查找nextFiber = fiber.type === workInProgress.type ? null : fiber.child;} else {// 往下遍历nextFiber = fiber.child;}// 根据nextFiber指针,继续往下遍历...}
}

从代码和注释我们知道,主要是调用propagateContextChange_eager函数来查找和通知子组件更新:

  • 通过workInProgress使用DFS深度优先算法遍历该组件下的所有子组件
  • 通过dependency.context === context知道当前fiber节点订阅了该上下文
    • 通过fiber.tag === ClassComponent处理类组件,会创建一个强制刷新任务ForceUpdate并添加到更新队列updateQueue中
    • 然后调用scheduleContextWorkOnParentPath向上遍历父路径上所有节点的fiber.childLanes属性, 表明其子节点有改动, 子节点会进入更新逻辑

scheduleContextWorkOnParentPath函数如下:主要就是按照父路径以此向上遍历,并更新node.childLanes属性,让React知道哪些节点需要更新,在后续协调阶段会统一将依赖该上下文的节点更新。

// 修改父路径上所有节点的fiber.childLanes属性, 表明其子节点有改动, 子节点会进入更新逻辑
export function scheduleContextWorkOnParentPath(parent: Fiber | null,renderLanes: Lanes,propagationRoot: Fiber,
) {// Update the child lanes of all the ancestors, including the alternates.let node = parent;while (node !== null) {const alternate = node.alternate;if (!isSubsetOfLanes(node.childLanes, renderLanes)) {node.childLanes = mergeLanes(node.childLanes, renderLanes);if (alternate !== null) {alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);}} else if (alternate !== null &&!isSubsetOfLanes(alternate.childLanes, renderLanes)) {alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);} else {// Neither alternate was updated.// Normally, this would mean that the rest of the// ancestor path already has sufficient priority.// However, this is not necessarily true inside offscreen// or fallback trees because childLanes may be inconsistent// with the surroundings. This is why we continue the loop.}if (node === propagationRoot) {break;}node = node.return;}
}

这里面涉及到isSubsetOfLanesmergeLanes两个函数,这两个函数主要是对优先级进行的检查和合并操作。因为一个节点可能有多个操作,所以需要有多个优先级标记,React内部使用二进制管理,当有多个操作时,会合并优先级。

export function isSubsetOfLanes(set: Lanes, subset: Lanes | Lane): boolean {return (set & subset) === subset;
}export function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {return a | b;
}

看代码可知isSubsetOfLanes函数主要是按位与操作 (&) 来检查一个 lane 是否是另一个 lane 的子集。它返回一个布尔值,表示 subset 中的所有位是否都在 set 中设置。

mergeLanes函数使用按位或操作 (|) 将两个 lanes 合并在一起。这意味着,如果一个节点有多个操作,这些操作的优先级会被合并到一个值中。

举个例子

假设你有一个组件树,根节点是 A,其子节点是 B 和 C,其中 C 的子节点是 D 和 E。如果 D 的上下文发生变化,需要更新,那么 scheduleContextWorkOnParentPath 会执行以下操作:

  • 检查节点 C:

    • isSubsetOfLanes(renderLanes, C.childLanes) 返回 false,因为 C 的 childLanes 可能还不包含 D 的更新优先级。
    • 合并 D 的更新优先级到 C 的 childLanes,即 C.childLanes = mergeLanes(C.childLanes, renderLanes)。
  • 检查节点 A:

    • 同样的逻辑,isSubsetOfLanes(renderLanes, A.childLanes) 返回 false。
    • 合并更新优先级到 A 的 childLanes,即 A.childLanes = mergeLanes(A.childLanes, renderLanes)。

isSubsetOfLanes 确保只有当需要更新时才会进行合并操作,而 mergeLanes 确保所有需要的更新优先级都被正确地标记在 childLanes 上。在后续的更新过程中,React 会使用这些标记来高效地确定哪些节点需要更新,从而实现高效的更新机制。

至此整个创建上下文以及上下文变化之后Provider会通知所有订阅该上下文的子组件进行更新的流程我们就梳理完了。在这里小结一下:使用createContext创建上下文后,会返回包含Provider、Consumer属性的上下文对象,React会根据其内部的$$typeof属性将该对象识别为ContextProvider/ContextConsumer类型的组件,当通过Provider的value属性修改上下文时,会在beginWork函数中调用updateContextProvider将newValue绑定到context.current上,并且会调用propagateContextChange_eager函数通过DFS算法来查找所有依赖该上下文的组件并根据父路径向上遍历通过mergeLanes更改parent.childLanes属性来表示其子组件需要更新,最后在协调阶段的fiber构造时对比更新

现在我们知道了创建和更新上下文的原理,下面我们来介绍下在组件中我们如何消费的原理。

一般我们在类组件通过Context.Consumer来消费上下文,在函数组件中通过useContext这个Hook来消费,所以下面从这两个API来入手介绍,其中这两个API都是使用相同的算法readContext来获取上下文的,所以下面先从useContext入手

useContext

先看useContext定义:

export function useContext<T>(Context: ReactContext<T>): T {const dispatcher = resolveDispatcher();return dispatcher.useContext(Context);
}

同其他Hooks一样,使用执行的函数会有dispatcher进行派发,但是context比较特殊,在Mount、Update都是执行的reactContext函数,下面看看该函数干了什么?

export function readContext<T>(context: ReactContext<T>): T {return readContextForConsumer(currentlyRenderingFiber, context);
}

readContext函数只是调用了readContextForConsumer,在其内部主要是创建了依赖列表(链表形式管理),以便当更新上下文时能找到依赖该上下文的组件。即上面propagateContextChange_eager函数中的dependency.context === context

function readContextForConsumer<T>(consumer: Fiber | null,context: ReactContext<T>
): T {// isPrimaryRenderer是否是主渲染,获取当前上下文的值const value = isPrimaryRenderer? context._currentValue: context._currentValue2;if (lastFullyObservedContext === context) {// 表示当前fiber依赖的上下文没有变化,不需要处理} else {const contextItem = {context: ((context: any): ReactContext<mixed>),memoizedValue: value,next: null, // 因为一个组件即fiber可以创建/依赖多个上下文,所以这里使用链表管理};if (lastContextDependency === null) {// This is the first dependency for this component. Create a new list.lastContextDependency = contextItem;consumer.dependencies = {lanes: NoLanes,firstContext: contextItem,};} else {// 以及有依赖上下文列表则直接添加lastContextDependency = lastContextDependency.next = contextItem;}}return value;
}

从代码中可知会根据当前上下文创建一个依赖快照contextItem并保存在依赖列表中fiber.dependencies,如果是第一个依赖则需要创建一个依赖列表,否则直接将当前创建的快照添加到依赖列表中即可。然后返回当前的上下文value

由此就能看出,useContext这个Hook接收一个上下文context然后返回上下文的值对象context._currentValue,即const value = useContext(context)。当上下文变化之后,Provider会在pushProvider函数中将context._currentValue修改为新的value值,然后通知依赖该上下文的组件更新,在该组件中通过context._currentValue获取新的value值。这里的上下文不会改变,变化的只是上下文中的value值,即_currentValue属性

Consumer

这个API主要在类组件中使用,是有createContext创建上下文之后自带的属性,其必须包裹JSX代码组件才能访问上下文信息,在上面的beginWork中得知,当使用Consumer时是执行的updateContextConsumer函数,简略代码如下:

function updateContextConsumer(current: Fiber | null,workInProgress: Fiber,renderLanes: Lanes,) {let context: ReactContext<any>;const consumerType: ReactConsumerType<any> = workInProgress.type;context = consumerType._context;const newProps = workInProgress.pendingProps;// 传入的render函数const render = newProps.children;const newValue = readContext(context);let newChildren;newChildren = render(newValue);// React DevTools reads this flag.workInProgress.flags |= PerformedWork;reconcileChildren(current, workInProgress, newChildren, renderLanes);return workInProgress.child;
}

从上面代码能看出来Consumer内部也是调用的readContext来获取上下文value的,然后将该value传入的render,这就是为什么使用Consumer消费上下文时,必须要使用Consumer包裹一个函数来使用上下文的原因。

总结

当使用上下文时,需要通过createContext来创建一个上下文,会返回一个包含$$typeof、Provider、Consumer属性的对象,React会根据$$typeofProvider、Consumer类型处理为组件,所以我们可以通过<Context.Provider><Context.Consumer>来使用,其中Provider作为上下文的提供方用于提供/更新共享的value,当上下文变化之后,Provider会在pushProvider函数中将context._currentValue修改为新的value值,然后利用DFS通知依赖该上下文的所有组件更新,在更新组件中执行readContext函数(Consumer和useContext都是使用的readContext获取上下文值)通过context._currentValue获取新的value值然后进行更新渲染。这里的上下文不会改变,变化的只是上下文中的value值,即_currentValue属性

具体详细流程如下:

  • 创建上下文:

    • 使用 createContext 创建包含 Provider 和 Consumer 的上下文对象。
  • 提供上下文值:

    • 在组件树中使用 Provider 组件,传递 value 给子组件。
  • 消费上下文值:

    • 使用 Consumer 组件或 useContext Hook 获取上下文值。
  • 上下文值变化时的处理:

    • 在 Provider 中,新的 value 传入后,会比较新旧值。
    • 如果值发生变化,会调用 propagateContextChange -> propagateContextChange_eager 函数。
    • propagateContextChange_eager 函数遍历组件树,找到依赖该上下文的组件,并标记这些组件进行更新。
    • 使用 scheduleContextWorkOnParentPath 函数更新父路径上的 childLanes 属性,以确保在后续渲染中处理更新。
  • 更新组件:

    • 在更新阶段,React 会检查每个 Fiber 节点的 lanes 和 childLanes 属性,确定需要更新的组件,并重新渲染这些组件。

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

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

相关文章

微信小程序删除滑块 SwiperCell 自动收起 Van weapp van-swipe-cell 滑块自动收起 点击页面也自动收起滑块

在当前页面整个 view 中 给页面绑定 点击事件bindtap"onSwipeCellPage"给 van-swipe-cell 组件设置 id &#xff08;for循环可以添加 id"swip-cell-{{item.id}}" &#xff09;van-swipe-cell 组件 添加属性 当用户打开滑块时触发 bind:open"swiperCel…

Ajax原理-XMLHttpRequest、Promise以及封装简易的axios函数

这里写目录标题 一级目录二级目录三级目录 一、Ajax原理-XMLHttpRequest对象1.步骤 二、XMLHttpRequest-查询参数1.定义2.语法 二、XMLHttpRequest-数据提交1.需求2.核心 三、Promise1.定义2.好处3.三种状态 四、封装简易的axios1.需求&#xff1a;2.封装axios函数获取数据3.封…

使用UDP套接字编程详解【C语言】

UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;是一种面向无连接的传输层协议&#xff0c;用于在计算机网络上发送数据。它与 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;相比具有轻量、高效的特点&…

英语(二)-我的学习方式

章节章节汇总我的学习方式历年真题作文&范文 目录 1、背单词 2、学语法 3、做真题 4、胶囊助学计划 写在最前&#xff1a;我是零基础&#xff0c;初二就听天书的那种。 本专栏持续更新学习资料 1、背单词 单词是基础&#xff0c;一定要背单词&#xff01;考纲要求要…

云动态摘要 2024-07-23

给您带来云厂商的最新动态,最新产品资讯和最新优惠更新。 最新优惠与活动 数据库上云优选 阿里云 2024-07-04 RDS、PolarDB、Redis、MongoDB 全系产品新用户低至首年6折起! [免费体验]智能助手ChatBI上线 腾讯云 2024-07-02 基于混元大模型打造,可通过对话方式生成可视化…

中文分词库 jieba 详细使用方法与案例演示

1 前言 jieba 是一个非常流行的中文分词库&#xff0c;具有高效、准确分词的效果。 它支持3种分词模式&#xff1a; 精确模式全模式搜索引擎模式 jieba0.42.1测试环境&#xff1a;python3.10.9 2 三种模式 2.1 精确模式 适应场景&#xff1a;文本分析。 功能&#xff1…

qt设置过滤器

1.创建事件过滤器类&#xff0c;在主窗口中安装事件过滤器 class PasteFilter : public QObject {Q_OBJECTpublic:PasteFilter(QObject *parent nullptr) : QObject(parent) {}protected:bool eventFilter(QObject *obj, QEvent *event) override {if (event->type() QEv…

【Zotero插件】Zotero Tag为文献设置阅读状态 win11下相关设置

【Zotero插件设置】Zotero Tag为文献设置阅读状态 win11下相关设置 1.安装Zotero Tag1.1安装1.2配置1.3 win11的相关设置1.3.1 字体安装 参考教程 2.支持排序的标注参考教程 1.安装Zotero Tag 1.1安装 Zotero Tag插件下载链接安装方法&#xff1a;Zotero–》工具–》附加组件…

googleTest 源码主线框架性分析——TDD 01

TDD&#xff0c;测试驱动开发&#xff0c;英文全称Test-Driven Development&#xff0c;简称TDD&#xff0c;是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码&#xff0c;然后只编写使测试通过的功能代码&#xff0c;通过测试来推…

苹果和乔布斯的传奇故事,从车库创业到万亿市值巨头

苹果公司的品牌故事&#xff0c;就像一部充满创新、挑战与辉煌的科幻大片&#xff0c;让人目不暇接。 故事始于1976年&#xff0c;那时&#xff0c;年轻的史蒂夫乔布斯与斯蒂夫沃兹尼亚克在加州的一个简陋车库里&#xff0c;用他们的热情和智慧&#xff0c;捣鼓出了世界上第一…

python学习之闭包与装饰器

一、闭包 闭包允许一个函数访问并操作函数外部的变量&#xff08;即父级作用域中的变量&#xff09;&#xff0c;即使在该函数外部执行。 特性&#xff1a; (1)外部函数嵌套内部函数。 (2)外部函数可以返回内部函数。 (3)内部函数可以访问外部函数的局部变量。 def out()…

《昇思 25 天学习打卡营第 18 天 | 扩散模型(Diffusion Models) 》

《昇思 25 天学习打卡营第 18 天 | 扩散模型&#xff08;Diffusion Models&#xff09; 》 活动地址&#xff1a;https://xihe.mindspore.cn/events/mindspore-training-camp 签名&#xff1a;Sam9029 扩散模型&#xff08;Diffusion Models&#xff09; 扩散模型概述 扩散模…

linux中使用docker安装mongodb

随着容器的普及&#xff0c;越来越多服务都喜欢跑在容器中&#xff0c;并且安装也很方便快捷&#xff0c;接下来一起看下linux中使用docker来安装mongodb吧&#xff01; 1.首先安装docker&#xff1b; 使用Yum 进行安装&#xff0c;我安装docker比较喜欢参考阿里云中的安装步骤…

通过泛型+函数式编程封装成通用解决方案|缓存穿透、缓存击穿,缓存雪崩

缓存更新方法封装 用到了泛型、函数式编程。 使用函数式编程是因为我们这个是一个通用的工具&#xff0c;使用泛型&#xff08;泛型&#xff08;Generics&#xff09; 允许我们定义类、接口和方法&#xff0c;可以使用不同类型的参数进行操作&#xff09;可以实现数据类型的通…

Mem0 - 个人 AI 的内存层

文章目录 一、关于 Mem0核心功能&#x1f511;路线图 &#x1f5fa;️常见用例Mem0与RAG有何不同&#xff1f; 二、快速入门 &#x1f680;1、安装2、基本用法&#xff08;开源&#xff09;3、高级用法&#x1f527;4、大模型支持 三、MultiOn1、概览2、设置和配置4、将记忆添加…

鸿蒙仓颉语言【模块module】

module 模块 模块配置文件&#xff0c;这里指项目的modules.json 文件&#xff0c;用于描述代码项目的基础元属性。 {"name": "file name", //当前项目的名称"description": "项目描述", //项目描述"version": "1.0…

人工智能与机器学习原理精解【4】

文章目录 马尔科夫过程论要点理论基础σ代数定义性质应用例子总结 马尔可夫过程概述一、马尔可夫过程的原理二、马尔可夫过程的算法过程三、具体例子 马尔可夫链的状态转移概率矩阵一、确定马尔可夫链的状态空间二、收集状态转移数据三、计算转移频率四、构建状态转移概率矩阵示…

视频汇聚平台EasyCVR启动出现报错“cannot open shared object file”的原因排查与解决

安防视频监控EasyCVR安防监控视频系统采用先进的网络传输技术&#xff0c;支持高清视频的接入和传输&#xff0c;能够满足大规模、高并发的远程监控需求。EasyCVR平台支持多种视频流的外部分发&#xff0c;如RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、WebRTC、fmp4等&#xf…

kafka基础介绍

一、为什么使用消息队列 1.使用同步的通信方式来解决多个服务之间的通信 同步的通信方式会存在性能和稳定性的问题。 2.使用异步的通信方式 针对于同步的通信方式来说,异步的方式,可以让上游快速成功,极大提高了系统的吞吐量。而且在分布式系统中,通过下游多个服务的 分布式事…

java爽看Stream

Java Stream MD笔记 一、Java Stream概念 Java Stream是Java 8引入的一种新的数据处理方式&#xff0c;提供了一种高效、便利的方法来处理集合数据。Stream流可以让开发人员以声明式的方式对数据进行操作&#xff0c;从而使代码更加简洁、易读。Stream本身不存储数据&#xf…