【React Hooks原理 - useState】

news2024/9/21 8:02:56

概述

useState赋予了Function Component状态管理的能力,可以让你在不编写 class 的情况下使用 state 。其本质上就是一类特殊的函数,它们约定以 use 开头。本文从源码出发,一步一步看看useState是如何实现以及工作的。

基础使用

function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] 

在组件顶层通过useState申明状态,useState接收一个参数作为该状态的初始值,该参数可以是任意类型的值,也可以是返回初始值的函数,React内部会进行判断然后执行并缓存该结果。返回一个包含当前状态和更新状态回调的数组

const [state, setState] = useState(initialState)

所有的Hooks都必须在组件顶层或者自定义Hook顶层中使用,不能在条件、循环语句中使用,这是为了避免Hook的位置混乱导致React无法正确找到状态和Hooks的对应而发生错误

源码解析

前面的文章我们提到过,我们使用的Hooks在React18之后在React内部拆分mount、update两个函数,并由dispatcher管理。下面我们将从入口、mount、update这三个方面来介绍useState的源码实现。

入口:文件路径在react/packages/react/src /ReactHooks.js

export function useState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

我们在组件内调用useState申明状态,实际就是执行的这个函数,然后通过dispatcher根据渲染阶段分发执行那个函数。

Mount挂载时

在挂载时,主要调用了mountStatemountStateImplmountWorkInProgressHook下面一一讲解。

mountState: 挂载时调用的函数,接收组件内传入的初始值initialState,然后返回当前状态和更新状态的回调

  • 调用mountStateImpl在当前渲染fiber节点中创建一个hook list用于对所有hook进行管理
  • 根据当前渲染fiber和更新队列创建一个dispatch用于更新状态,由于将dispatch绑定到dispatchSetState上,所以当我们通过set函数更新时,实际执行的是dispatchSetState函数
  • 将dispatch挂载到queue更新队列中,便于后续更新时候直接调用dispatch更新
  • 返回包含当前状态和更新回调的数组
function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountStateImpl(initialState);
  const queue = hook.queue;
  // 将dispatch绑定到dispatchSetState,所以当我们通过set函数更新时,实际执行的是dispatchSetState函数
  const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue,
  ): any);
  queue.dispatch = dispatch;
  return [hook.memoizedState, dispatch];
}

hook和queue的数据结构如下:

hook = {
   memoizedState: initialValue, // 保存当前的状态值
   baseState: initialValue,     // 初始状态,用于批处理
   baseQueue: null,				// 需要更新的队列
   queue: null,                 // 更新队列,用于存储状态更新
   next: null                   // 链表中的下一个 Hook 节点
};

const queue = {
    pending: null, // 指向尚未处理的更新。这些更新将在下一次渲染时被处理。
    lanes: NoLanes, // 优先级
    dispatch: null, // 用于分发动作(actions)触发状态更新。
    lastRenderedReducer: basicStateReducer, // 最后一次渲染时使用的 reducer 函数。对于 useReducer,它是用户定义的 reducer 函数;对于 useState,它是一个内置的基本状态 reducer
    lastRenderedState: (initialState: any), // 保存了上一次渲染时 Hook 的状态,以便在下一次渲染时能够对比新旧状态并进行必要的更新。
  };

在mountStateImpl函数中:

  • 调用mountWorkInProgressHook生成hook
  • 缓存初始值(如果是函数则执行并缓存结果),其中memoizedState会在update时计算新值,而baseState则记录初始值在批处理的时候会使用
  • 基于生成的hook初始化更新队列queue
  • 返回这个hook链表给到mountState继续处理
function mountStateImpl<S>(initialState: (() => S) | S): Hook {
  const hook = mountWorkInProgressHook();
  if (typeof initialState === 'function') {
    const initialStateInitializer = initialState;
    // $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
    initialState = initialStateInitializer();
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;
  return hook;
}

mountWorkInProgressHook主要就是创建初始化一个hook链表,并将其挂载到当前渲染的fiber节点中。

function mountWorkInProgressHook(): Hook {
  const hook: Hook = {
    memoizedState: null,

    baseState: null,
    baseQueue: null,
    queue: null,

    next: null,
  };

  if (workInProgressHook === null) {
    // This is the first hook in the list
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

至此useState在挂载时的整个流程就完成了,我们在这里小结一下。当我们在组件中使用useState时,其背后流程就是: Function Component -> FIber节点 -> Hooks链表 -> UpdateQueue -> dispatch(updateState)。每个函数组件都有一个对应的 Fiber 节点,每个 Fiber 节点都有一个 Hook 链表(比如保存组件中的useState、useEffect等所有hook),用于存储该组件中的所有 Hook。而Hook链表中的每个Hook都有一个UpdateQueue更新队列来对状态进行更新,在更新时会依次遍历这个Hooks链表然后执行对应Hook的更新。

fiber、hook、queue关系图:
在这里插入图片描述

Update更新时

上面我们介绍了在使用useState初始挂载一个状态时做了什么工作,现在来看看当组件重新渲染时useState是怎么实现的。

function updateState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  return updateReducer(basicStateReducer, initialState);
}

从源码来看,如果使用过redux的同学可能会很熟悉,就和我们理解的一致也是通过dispatch来调用Reducer来进行updateState的。由此能看出useState 是基于 useReducer 实现,通过调用updateReducer来实现state更新,其中basicStateReducer 是React内部默认的处理状态更新的reducer。

function updateReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: (I) => S
): [S, Dispatch<A>] {
  const hook = updateWorkInProgressHook();
  return updateReducerImpl(hook, ((currentHook: any): Hook), reducer);
}

通过调用updateReducer将用于状态更新的reducer和初始值以及通过updateWorkInProgressHook处理后的链表传递给了updateReducerImpl进行处理。由于updateReducerImpl中代码比较多而且还涉及到了Scheduler调度中的优先级,所以对于部分跳过的更新逻辑在这里进行了省略,有兴趣的可以去官网查看【React Github】

updateWorkInProgressHook代码如下:该函数主要逻辑就是复用已有的hook并更新workInProgressHook指针指向下一个hook。优先复用当前渲染中的hook即workInProgress树中当前fiber的hook,如果没有则克隆页面显示的current fiber中的hook,如果都没有则通过throw抛出异常

updateWorkInProgressHook函数处理并更新指针到下一个hook是因为当前hook在上一次渲染或挂载过程中已经执行并存储了状态。这是因为React需要保持对hook链表的引用,以便在下一次渲染时可以复用这些hook。删除hook可能会导致状态丢失和链表结构破坏。所以在React的实现中,不会在上一次渲染时删除hook,而是在下一次渲染时更新指针。

function updateWorkInProgressHook(): Hook {
let nextCurrentHook: null | Hook; // 下次更新的hook
// nextCurrentHook值为当前渲染中fiber的下一次hook或者复用页面显示的当前fiber的下一个hook
if (currentHook === null) {
   // 通过alternate指针切换workInProgress和current fiber树
    const current = currentlyRenderingFiber.alternate;
    if (current !== null) {
    nextCurrentHook = current.memoizedState;
    } else {
    nextCurrentHook = null;
    }
} else {
    nextCurrentHook = currentHook.next;
}

let nextWorkInProgressHook: null | Hook;
if (workInProgressHook === null) {
    nextWorkInProgressHook = currentlyRenderingFiber.memoizedState;
} else {
    nextWorkInProgressHook = workInProgressHook.next;
}

if (nextWorkInProgressHook !== null) {
    // There's already a work-in-progress. Reuse it.
    workInProgressHook = nextWorkInProgressHook;
    nextWorkInProgressHook = workInProgressHook.next;

    currentHook = nextCurrentHook;
} else {
    // Clone from the current hook.

    if (nextCurrentHook === null) {
    const currentFiber = currentlyRenderingFiber.alternate;
    if (currentFiber === null) {
        // This is the initial render. This branch is reached when the component
        // suspends, resumes, then renders an additional hook.
        // Should never be reached because we should switch to the mount dispatcher first.
        throw new Error(
        'Update hook called on initial render. This is likely a bug in React. Please file an issue.',
        );
    } else {
        // This is an update. We should always have a current hook.
        throw new Error('Rendered more hooks than during the previous render.');
    }
    }
    currentHook = nextCurrentHook;
    const newHook: Hook = {
    memoizedState: currentHook.memoizedState,
    baseState: currentHook.baseState,
    baseQueue: currentHook.baseQueue,
    queue: currentHook.queue,
    next: null,
    };
    if (workInProgressHook === null) {
    // This is the first hook in the list.
    currentlyRenderingFiber.memoizedState = workInProgressHook = newHook;
    } else {
    // Append to the end of the list.
    workInProgressHook = workInProgressHook.next = newHook;
    }
}
return workInProgressHook;
}

updateReducerImpl代码如下:

/**
 *
 * hook:指向当前 Fiber 节点正在处理的具体 Hook 实例(即 Hook 链表中的一个节点)。
 * current:指向当前 Fiber 节点中对应的 Hook 实例的当前状态(即已渲染到页面上的状态)。
 * reducer触发active进行state的更新
 */
function updateReducerImpl<S, A>(
  hook: Hook,
  current: Hook,
  reducer: (S, A) => S
): [S, Dispatch<A>] {
  // 获取当前指向hook的更新队列,以及绑定reducer更新函数
  const queue = hook.queue;
  queue.lastRenderedReducer = reducer;
  let baseQueue = hook.baseQueue;

  // 如果有上次渲染未处理的更新队列
  const pendingQueue = queue.pending;
  if (pendingQueue !== null) {
    // 有上次为处理的更新以及本次也有需要处理的更新,则将两个更新队列合并,否则将上次未处理的赋值给更新队列等待本次渲染更新
    if (baseQueue !== null) {
      const baseFirst = baseQueue.next;
      const pendingFirst = pendingQueue.next;
      baseQueue.next = pendingFirst;
      pendingQueue.next = baseFirst;
    }
    current.baseQueue = baseQueue = pendingQueue;
    queue.pending = null;
  }

  // 如果本次没有更新队列,则更新memoizedState为baseState
  const baseState = hook.baseState;
  if (baseQueue === null) {
    hook.memoizedState = baseState;
  } else {
    // 更新队列有状态需要更新
    const first = baseQueue.next;
    let newState = baseState;

    let newBaseState = null;
    let newBaseQueueFirst = null;
    let newBaseQueueLast: Update<S, A> | null = null;
    let update = first;
    let didReadFromEntangledAsyncAction = false;
    do {
      const updateLane = removeLanes(update.lane, OffscreenLane);
      const isHiddenUpdate = updateLane !== update.lane;
      const shouldSkipUpdate = isHiddenUpdate
        ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
        : !isSubsetOfLanes(renderLanes, updateLane);
      // 根据优先级判断当前是否需要跳过更新,并保存在newBaseQueueLast中在下次渲染时更新,然后调用markSkippedUpdateLanes跳过本次更新
      if (shouldSkipUpdate) {
        ...
      } else {
        const revertLane = update.revertLane;
        // 根据优先级判断当前是否需要跳过更新,并保存在newBaseQueueLast中在下次渲染时更新
        if (!enableAsyncActions || revertLane === NoLane) {
          ...
        } else {
          // 将符合本次更新条件的状态保存在update链表中,等待更新
          if (isSubsetOfLanes(renderLanes, revertLane)) {
            update = update.next;
            if (revertLane === peekEntangledActionLane()) {
              didReadFromEntangledAsyncAction = true;
            }
            continue;
          } else {
            // 不符合的保存在newBaseQueueLast等待下次渲染时候更新
            ...
          }
        }

        // 开始更新,当前update对象是否提前计算,否则通过reducer处理
        const action = update.action;
        if (update.hasEagerState) {
          // If this update is a state update (not a reducer) and was processed eagerly,
          // we can use the eagerly computed state
          newState = ((update.eagerState: any): S);
        } else {
          newState = reducer(newState, action);
        }
      }
      update = update.next;
    } while (update !== null && update !== first);

    // 遍历本次更新队列之后,判断是否有跳过的更新,如果有则保存在newBaseState中,等待下次渲染时更新
    if (newBaseQueueLast === null) {
      newBaseState = newState;
    } else {
      newBaseQueueLast.next = (newBaseQueueFirst: any);
    }

    // 判断上一次的状态和reducer更新之后的状态是否一致,发生变化则通过markWorkInProgressReceivedUpdate函数给当前fiber打上update标签
    if (!is(newState, hook.memoizedState)) {
      markWorkInProgressReceivedUpdate();
      // 检查异步处理操作,如果说是异步获取值,则需要通过throw entangledActionThenable将当前更新挂起
      if (didReadFromEntangledAsyncAction) {
        const entangledActionThenable = peekEntangledActionThenable();
        if (entangledActionThenable !== null) {
          throw entangledActionThenable;
        }
      }
    }

    // 将本次新的state保存在memoizedState中
    hook.memoizedState = newState;
    // 保存下次更新的初始值,如果本次没有跳过更新,该值为更新后通过reducer或者eagerState计算的新值,有跳过的更新则会本次更新前原来的初始值
    hook.baseState = newBaseState;
    // 将本次跳过的更新保存在baseQueue更新队列中中,下次渲染时更新
    hook.baseQueue = newBaseQueueLast;

    queue.lastRenderedState = newState;
  }

  // 没有状态更新时,将当前队列优先级设置为默认
  if (baseQueue === null) {
    queue.lanes = NoLanes;
  }

  const dispatch: Dispatch<A> = (queue.dispatch: any);
  return [hook.memoizedState, dispatch];
}

根据上面代码以及注释可知,在updateReducerImpl主要做了一下功能:

  • 获取和合并更新队列。
  • 遍历更新队列,根据优先级判断是否需要跳过,计算新的更新队列
  • 通过reducer进行状态更新
  • 更新基础状态和队列
  • 处理异步操作的影响
  • 返回新的状态和 dispatch 函数

useState于update的差异

根据下面表格可以简要总结两者差异主要是:mountState进行初始化挂载,updateState在其基础上进行更新队列处理,包括跳过低优先级任务以及提前处理任务。

比较点mountStateupdateState
创建 Hook 和更新队列
返回值返回包含当前状态值和 set 函数的数组返回包含当前状态值和 set 函数的数组
用途主要用于组件的首次渲染,初始化数据挂载处理组件的更新渲染,包含对上次渲染跳过的部分进行处理
初始状态处理计算并设置初始状态(如果 initialState 是函数,会调用它)不处理初始状态,只处理更新
更新队列创建新的更新队列,并将其分配给 Hook处理现有更新队列,可能需要合并新的和已有的更新队列
状态计算直接设置初始状态通过调用 reducer(通常是 basicStateReducer)计算新状态
Hook 链表将 Hook 添加到当前 Fiber 的 Hook 链表中更新当前 Fiber 的 Hook 链表中的现有 Hook
优先级处理不涉及优先级处理可能需要处理优先级和跳过的更新

set函数

由上面知道了,使用useSatte会返回包含[newState,setValue]的数组,然后我们调用setValue可以更新状态值,而这个dispatch实际是执行的dispatchSetState(在mountState中通过bind进行了绑定),所以下面来看看在dispatchSetState函数中做了什么?

// 触发更新后,React会判断当前是否还有其他渲染或者挂起,没有会提前计算值,不变则跳过更新
function dispatchSetState<S, A>(
    fiber: Fiber,
    queue: UpdateQueue<S, A>,
    action: A,
  ): void {
    // 获取优先级
    const lane = requestUpdateLane(fiber);
    
    // 创建一个update更新对象
    const update: Update<S, A> = {
      lane,
      revertLane: NoLane,
      action,
      hasEagerState: false,
      eagerState: null,
      next: (null: any),
    };
    // 当前是否是渲染阶段
    if (isRenderPhaseUpdate(fiber)) {
        // 申请加入正在渲染的更新队列
      enqueueRenderPhaseUpdate(queue, update);
    } else {
        // 页面上显示的fiber树 - 双缓冲树,通过alternate切换指针
      const alternate = fiber.alternate;
      // 判断current树和workInProgress树是否有挂起的更新,如果没有则进入提前计算逻辑
      if (
        fiber.lanes === NoLanes &&
        (alternate === null || alternate.lanes === NoLanes)
      ) {
        const lastRenderedReducer = queue.lastRenderedReducer;
        if (lastRenderedReducer !== null) {
          let prevDispatcher = null;
          try {
            const currentState: S = (queue.lastRenderedState: any);
            // 用于update state的reducer,由React内部提供,所以这里使用缓存的lastRenderedReducer可以提高性能
            const eagerState = lastRenderedReducer(currentState, action);
            // 标识该值已经提前计算
            update.hasEagerState = true;
            update.eagerState = eagerState;
            if (is(eagerState, currentState)) {
                // 无更新则跳过更新步骤
              enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
              return;
            }
          } catch (error) {
            // Suppress the error. It will throw again in the render phase.
          } finally {
          }
        }
      }
      
      // 进入组件更新步骤 enqueueConcurrentHookUpdate会将update更新对象添加到enqueueUpdate更新队列中
      const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
      if (root !== null) {
        // 进入Scheduler调度阶段,进行fiber构造
        scheduleUpdateOnFiber(root, fiber, lane);
        entangleTransitionUpdate(root, queue, lane);
      }
    }
}

从代码中能知道在函数中主要:

  • 创建一个带有优先级的update对象
  • 判断当前是否是渲染更新阶段,如果是则直接将update对象添加到更新队列中等待更新,否则判断current树和workInProgress树是否有挂起的更新,如果没有则进入提前计算并判断值是否变化,没有变化则跳过更新,否则就进入组件更新步骤将update对象添加到enqueueUpdats更新队列中然后调用scheduleUpdateOnFiber等待调度更新

其中enqueueConcurrentHookUpdate函数主要就是将创建的update对象添加到当前fiber的更新队列enqueueUpdate中,其中通过MapSet来对渲染更新数据进行管理

function enqueueRenderPhaseUpdate<S, A>(
    queue: UpdateQueue<S, A>,
    update: Update<S, A>,
  ) {
   if (renderPhaseUpdates === null) {
     renderPhaseUpdates = new Map();
   }
   let firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
   if (firstRenderPhaseUpdate === undefined) {
     renderPhaseUpdates.set(queue, update);
   } else {
     // Append the update to the end of the list.
     let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
     while (lastRenderPhaseUpdate.next !== null) {
       lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
     }
     lastRenderPhaseUpdate.next = update;
   }
 }

将本次更新的update对象添加到更新队列中,会调用scheduleUpdateOnFiber等待调度更新然后进行新的fiber树构造。

这里从宏观角度,简单说下React多个包之间的流程来帮助我们理解。 React中有几个核心包:react、react-dom、react-reconciler(协调)、scheduler(调度),其中上面的update更新对象处理以及将其添加到更新队列中是在react包中处理的,然后调用scheduler包中的scheduleUpdateOnFiber等待调度进入react-reconciler包中处理进行renderer阶段进行fiber构造,最后进入react-dom进行commit阶段进行页面的渲染。
在这里插入图片描述
上图就是React总的流程图,各个核心流程都在里面,有兴趣的同学可以查看我写的其他React源码系列,比如【React架构 - Fiber构造循环】

总结

上面说了这么多这里进行简单总结一下,有的点可能会多次提及为了巩固记忆。

正文开始~~

在页面渲染过程中有两个阶段分别为mount(首次渲染)、update(更新渲染),而React为了更好的管理和优化副作用将Hooks(useContext除外)拆为了mount、update两个函数。通过内置的dispatcher管理,React会根据目前具体处于什么阶段来决定调用那个函数,比如在mount阶段,会调用mountState函数,这对于开发者来说是无感的,React在内部进行了映射。具体的流程走向如下图所示

mount、update时,state的处理:
在这里插入图片描述

当触发set函数进行状态更新时:

在这里插入图片描述
以上都是根据自己理解进行总结梳理的,如果理解有误还请评论指正。

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

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

相关文章

高盛开源的量化金融 Python 库

GS Quant GS Quant是用于量化金融的Python工具包&#xff0c;建立在世界上最强大的风险转移平台之一之上。旨在加速量化交易策略和风险管理解决方案的开发&#xff0c;凭借25年的全球市场经验精心打造。 它由高盛的定量开发人员&#xff08;定量&#xff09;创建和维护&#…

Java内存划分详解:从基础到进阶

Java内存划分详解&#xff1a;从基础到进阶 1. 程序计数器&#xff08;Program Counter Register&#xff09;2. Java虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;3. 堆&#xff08;Heap&#xff09;4. 方法区&#xff08;Method Area&#xff09;5. 运行时常量…

网络编程的学习之udp

Udp编程过程 Sento不会阻塞 实现聊天室效果 上线 聊天 下线 服务端需要一个地址&#xff0c;去保留名字和ip地址 交互的时候发结构体 下面这个宏只能在c语言里使用 ser.sin_port htons(50000); 上面是端口号50000以上&#xff0c;两边要一样 这里是不要让udp发的太快&am…

YOLOv10改进 | Conv篇 | CVPR2024最新DynamicConv替换下采样(解决低FLOPs陷阱)

一、本文介绍 本文给大家带来的改进机制是CVPR2024的最新改进机制DynamicConv其是CVPR2024的最新改进机制&#xff0c;这个论文中介绍了一个名为ParameterNet的新型设计原则&#xff0c;它旨在在大规模视觉预训练模型中增加参数数量&#xff0c;同时尽量不增加浮点运算&#x…

imx6ull/linux应用编程学习(17)利用mqtt上传开发板数据,和控制开发板led(基于正点)

1.关于如何创建自己的服务器&#xff0c;可看上篇文章 imx6ull/linux应用编程学习&#xff08;16&#xff09;emqx &#xff0c;mqtt创建连接mqtt.fx-CSDN博客 2.实现任务&#xff1a;&#xff08;正点原子教程源码改&#xff09; (1)用户可通过手机或电脑远程控制开发板上的…

微软Win11 24H2七月更新补丁KB5040435发布!附下载

系统之家于7月10日发出最新报道&#xff0c;微软为Win11用户发布了24H2版本七月的最新更新补丁KB5040435。用户升级系统后&#xff0c;会发现版本号升至 26100.1150。此次更新针对远程身份验证拨入用户服务(RADIUS)协议与 MD5冲突等问题进行修复。接下来跟随小编看看此次更新的…

利用 AI 解放双手:把“贾维斯”带进现实 | 开源专题 No.64

Significant-Gravitas/AutoGPT Stars: 160k License: MIT AutoGPT 是开源 AI 代理生态系统的核心工具包。 提供构建、测试和委托 AI 代理的工具。AutoGPT 处于 AI 创新前沿&#xff0c;提供文档、贡献指南以及快速开始创建自己的代理。包含强大的组件如 Forge 和 Benchmark&…

Ollama完整教程:本地LLM管理、WebUI对话、Python/Java客户端API应用

老牛同学在前面有关大模型应用的文章中&#xff0c;多次使用了Ollama来管理和部署本地大模型&#xff08;包括&#xff1a;Qwen2、Llama3、Phi3、Gemma2等&#xff09;&#xff0c;但对Ollama这个非常方便管理本地大模型的软件的介绍却很少。 目前&#xff0c;清华和智谱 AI 联…

视图库对接系列(GA-T 1400)十四、视图库对接系列(本级)新增、修改订阅

说明 之前我们已经对接的设备,设备的话比较简单,是设备主动推送数据到平台的。 相信大家已经会了,那今天开始的话,我们来做对接平台,相对难点点。 但搞懂了核心的订阅流程的话,其实就不难了。 对接平台 订阅接口 订阅接口的话,有几个,添加、查询、更新、删除、取消…

【MOT】《Multiple Object Tracking in Recent Times: A Literature Review》

原文 Bashar M, Islam S, Hussain K K, et al. Multiple object tracking in recent times: A literature review[J]. arXiv preprint arXiv:2209.04796, 2022.https://arxiv.org/pdf/2209.04796 参考文章 多目标跟踪最新综述&#xff08;基于Transformer/图模型/检测和关联…

RK3568平台(vendor篇)vendor storage分区

一.简介 rockchip vendor storage是一种用于存储SN, MAC, LAN, BT等数据的区域&#xff0c;它具有不会丢失和系统启动各个阶段都可以访问的特性。它使用GPT分区表格式&#xff0c;并在U-boot, kernel和用户空间中提供了相应的驱动文件和接口。 rockchip vendor storage是一种特…

硅纪元AI应用推荐 | 百度橙篇成新宠,能写万字长文

“硅纪元AI应用推荐”栏目&#xff0c;为您精选最新、最实用的人工智能应用&#xff0c;无论您是AI发烧友还是新手&#xff0c;都能在这里找到提升生活和工作的利器。与我们一起探索AI的无限可能&#xff0c;开启智慧新时代&#xff01; 百度橙篇&#xff0c;作为百度公司在202…

软航文档控件VUE示例运行及控件替换方法记录

目录 示例运行 步骤一、npm install 步骤二、npm run dev 软航文档控件替换 附 vue小白记录一下软航文档控件VUE示例的运行方法以及示例中控件的替换过程。 示例运行 在已经安装好VUE环境的电脑上&#xff0c;VUE环境部署可以参考另一篇&#xff1a;配置VUE环境过程中 …

3.相机标定原理及代码实现(opencv)

1.相机标定原理 相机参数的确定过程就叫做相机标定。 1.1 四大坐标系及关系 &#xff08;1&#xff09;像素坐标系&#xff08;单位&#xff1a;像素&#xff08;pixel&#xff09;&#xff09; 像素坐标系是指相机拍到的图片的坐标系&#xff0c;以图片的左上角为坐标原点&a…

nvm安装使用 nrm使用

因维护老项目及开发新项目同时进行&#xff0c;需要使用不同版本的node进行运行&#xff0c;所以用nvm进行多个版本的node维护&#xff0c;通过nrm进行镜像源管理切换 简介 Node.js 是一种基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;用于构建高性能的网络应用程序…

mobx学习笔记

mobx介绍 mobx是一个功能强大&#xff0c;上手容易的状态管理工具。MobX背后的哲学很简单:任何源自应用状态的东西都应该自动地获得。利用getter和setter来收集组件的数据依赖关系&#xff0c;从而在数据发生变化的时候精确知道哪些组件需要重绘。 mobx和redux的区别 mobx更…

javaweb学习day1《HTML篇》--新浪微博(前端页面的创建思路及其HTML、css代码详解)

一、前言 本篇章为javaweb的开端&#xff0c;也是第一篇综合案例&#xff0c;小编也是看着黑马程序员的视频对里面的知识点进行理解&#xff0c;然后自己找一个新浪微博网页看着做的&#xff0c;主要还是因为懒&#xff0c;不想去领黑马程序员的资料了。 小编任务javaweb和ja…

人工智能算法工程师(中级)课程6-sklearn机器学习之聚类问题与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程6-sklearn机器学习之聚类问题与代码详解。在机器学习领域&#xff0c;聚类是一种无监督学习方法&#xff0c;旨在将相似的数据点划分为同一类别。sklearn是一个广泛应用于机器学习的Py…

初识C++语言(1)

目录 C语言简介 C 语言概述 C 语言的特点 语言简洁紧凑&#xff0c;使用灵活方便 运算符丰富 数据结构丰富 结构化语言 生成的代码质量高 可移植性强 C程序结构 C语言系统的使用 一.启动Dev-C 二、新建源程序 三…

【观察】甲骨文:用“SQL”实现AI的“融会贯通”,打通应用落地的“最后一公里”...

从2022年的ChatGPT&#xff0c;到2024年的Sora&#xff0c;生成式AI和大模型技术正以不可思议的发展速度颠覆着我们的认知。刚刚过去的一年&#xff0c;国内的“百模大战”更让大模型站上了市场“风口”&#xff0c;通过更为泛化的能力&#xff0c;赋予了千行万业数智化无限的想…