React 底层运行原理框架(大白话版)

面试题

  1. react有哪几种任务优先级?
  2. react hook的原理以及为什么只能在写在组件里,不能写在if else里面?

一、整体架构概览

React 的核心可以比喻成一条流水线工厂

arduino 复制代码
用户操作/状态更新
       ↓
┌─────────────────┐
│   Scheduler     │  ← 调度器:决定什么时候工作(优先级管理)
│  (时间切片)     │
└────────┬────────┘
         ↓md
┌─────────────────┐
│   Reconciler    │  ← 协调器:决定做什么工作(Diff 比较)
│  (Fiber 架构)   │
└────────┬────────┘
         ↓
┌─────────────────┐
│    Renderer     │  ← 渲染器:实际执行工作(DOM 操作)
│  (平台相关)     │
└────────┬────────┘
         ↓
    更新界面

三大核心模块:

  1. Scheduler - 调度器:管理任务的优先级和执行时机
  2. Reconciler - 协调器:计算新老虚拟 DOM 的差异(Diff)
  3. Renderer - 渲染器:将计算结果应用到真实 DOM

二、Scheduler(调度器)- 聪明的时间管理者

2.1 为什么需要 Scheduler?

想象一下,如果 React 一次性处理大量更新,浏览器就会卡住(掉帧)。Scheduler 就像一个智能管家,把大任务切成小块,让浏览器有时间绘制页面。

2.2 核心概念

javascript 复制代码
// 任务优先级(从高到低)
ImmediatePriority   // 立即执行,如用户输入
UserBlockingPriority  // 用户阻塞级,如点击、滚动
NormalPriority     // 正常优先级,如网络请求回调
LowPriority        // 低优先级,如分析统计
IdlePriority       // 空闲时才执行

2.3 核心函数流程

scss 复制代码
scheduleCallback(priority, callback)
       ↓
┌─────────────────┐
│  创建任务对象    │
│ { callback,     │
│   priority,     │
│   expirationTime│
│ }               │
└────────┬────────┘
         ↓
┌─────────────────┐
│  放入任务队列    │
│ (最小堆结构)     │
└────────┬────────┘
         ↓
┌─────────────────┐
│ requestIdleCallback│
│ 或 MessageChannel │ ← 浏览器空闲时执行
└────────┬────────┘
         ↓
   flushWork()
         ↓
 workLoop(hasTimeRemaining)

2.4 时间切片(Time Slicing)原理

javascript 复制代码
// 简化的工作循环
function workLoop(hasTimeRemaining) {
  while (currentTask !== null && hasTimeRemaining()) {
    // 执行当前任务
    currentTask.callback();

    // 检查时间是否够用(默认 5ms)
    if (!hasTimeRemaining()) {
      // 时间不够了,让出主线程
      break;
    }
  }

  // 还有任务没完成,继续调度
  if (currentTask !== null) {
    scheduleCallback(currentTask.priority, workLoop);
  }
}

通俗解释:

  • 就像吃面条,如果一口气吃完会噎着
  • Scheduler 把面条分成小段,吃一口歇一口
  • 浏览器每 16.6ms 渲染一帧,React 利用空闲时间工作

三、Reconciler(协调器)- 精明的差异计算师

3.1 什么是 Reconciler?

Reconciler 是 React 的大脑,负责比较新老虚拟 DOM,找出最小化的变更操作。

3.2 Fiber 架构

React 16 之前使用递归遍历,可能会卡死主线程。Fiber 把递归改成链表遍历,可以随时中断和恢复。

javascript 复制代码
// Fiber 节点结构
interface Fiber {
  // 类型信息
  tag: WorkTag;           // 组件类型(函数组件、类组件、HostDOM 等)
  type: any;              // 具体的类型(如 'div' 或函数)
  key: null | string;     // diff 的 key

  // 树结构(链表)
  return: Fiber | null;   // 父节点
  child: Fiber | null;    // 第一个子节点
  sibling: Fiber | null;  // 下一个兄弟节点

  // 状态
  pendingProps: any;      // 新 props
  memoizedProps: any;     // 当前 props
  memoizedState: any;     // 当前 state / hooks 链表头

  // 副作用
  flags: Flags;           // 标记需要执行的操作(插入、更新、删除)
  updateQueue: any;       // 更新队列

  // 双缓冲
  alternate: Fiber | null; // 指向 current tree 的对应节点
}

3.3 双缓冲机制(Double Buffering)

css 复制代码
Current Tree (当前显示)     WorkInProgress Tree (正在构建)
       ↓                            ↓
   ┌──────┐                    ┌──────┐
   │ Div  │ ← alternate →      │ Div  │
   └──┬───┘                    └──┬───┘
      │                           │
   ┌──┴──┐                     ┌──┴──┐
   │ Span│ ← alternate →       │ Span│
   └─────┘                     └─────┘

更新完成后:
WorkInProgress Tree 变成 Current Tree
(只需切换 root 指针,原子操作)

3.4 Reconciler 核心流程

scss 复制代码
开始更新
   ↓
┌─────────────────┐
│ createUpdate()  │ ← 创建更新对象
└────────┬────────┘
         ↓
┌─────────────────┐
│ enqueueUpdate() │ ← 加入组件的更新队列
└────────┬────────┘
         ↓
┌─────────────────┐
│ scheduleUpdateOnFiber() │ ← 调度更新
└────────┬────────┘
         ↓
┌─────────────────┐
│ performSyncWorkOnRoot   │ ← 同步执行(高优先级)
│ 或                     │
│ performConcurrentWorkOnRoot │ ← 并发执行(可中断)
└────────┬────────┘
         ↓
┌─────────────────┐
│ renderRootSync / │ ← 进入 render 阶段
│ renderRootConcurrent
└────────┬────────┘
         ↓
┌─────────────────┐
│ workLoopSync /   │ ← 工作循环
│ workLoopConcurrent
└────────┬────────┘
         ↓
    performUnitOfWork()  ← 处理每个 Fiber 节点(循环)
         ↓
    beginWork()          ← 向下遍历,创建/复用 Fiber
         ↓
    completeWork()       ← 向上回溯,收集副作用
         ↓
   commitRoot()          ← 进入 commit 阶段

3.5 详细函数说明

performUnitOfWork - 工作单元执行

javascript 复制代码
function performUnitOfWork(unitOfWork: Fiber): Fiber | null {
  // 1. 处理当前节点(创建/复用子 Fiber)
  const next = beginWork(unitOfWork);

  // 2. 如果有子节点,继续向下
  if (next !== null) {
    return next;
  }

  // 3. 没有子节点,开始向上回溯
  let completedWork = unitOfWork;
  do {
    // 完成当前节点的处理
    completeWork(completedWork);

    // 4. 如果有兄弟节点,处理兄弟
    const siblingFiber = completedWork.sibling;
    if (siblingFiber !== null) {
      return siblingFiber;
    }

    // 5. 否则继续向上
    completedWork = completedWork.return;
  } while (completedWork !== null);

  return null;
}

beginWork - 向下遍历阶段

javascript 复制代码
function beginWork(current, workInProgress, renderLanes) {
  const tag = workInProgress.tag;

  switch (tag) {
    case FunctionComponent:
      // 函数组件:执行函数,获取返回值
      return updateFunctionComponent(current, workInProgress);

    case ClassComponent:
      // 类组件:执行 render 方法
      return updateClassComponent(current, workInProgress);

    case HostRoot:
      // 根节点
      return updateHostRoot(current, workInProgress);

    case HostComponent:
      // 原生 DOM(div, span 等)
      return updateHostComponent(current, workInProgress);

    case HostText:
      // 文本节点
      return updateHostText(current, workInProgress);

    // ... 其他类型
  }
}

completeWork - 向上回溯阶段

javascript 复制代码
function completeWork(current, workInProgress) {
  const tag = workInProgress.tag;

  switch (tag) {
    case HostComponent:
      // 创建/更新 DOM 节点(但不挂载)
      const instance = createInstance(type, props);
      workInProgress.stateNode = instance;
      break;

    case HostText:
      // 创建文本节点
      const textInstance = createTextInstance(newText);
      workInProgress.stateNode = textInstance;
      break;
  }

  // 收集副作用,构建 effect 链表
  bubbleProperties(workInProgress);
}

四、Renderer(渲染器)- 勤劳的 DOM 操作员

4.1 Renderer 的职责

Renderer 是实际干活的模块,负责把 Reconciler 计算出的变更应用到真实 DOM。

4.2 Commit 阶段(同步不可中断)

markdown 复制代码
┌─────────────────────────────────────────────────────┐
│                    commitRoot                        │
│                  (同步执行)                          │
└─────────────────────────────────────────────────────┘
                         ↓
    ┌────────────────────┼────────────────────┐
    ↓                    ↓                    ↓
┌─────────┐      ┌─────────────┐      ┌─────────────┐
│  Before  │      │    Mutation │      │    Layout   │
│  Mutation│  →   │   (变更)   │  →   │  (布局)    │
│          │      │             │      │             │
│ 准备阶段 │      │ 操作真实 DOM │      │ 读取布局信息 │
│ 触发快照 │      │             │      │ 执行副作用   │
│ 触发 will│      │             │      │ 调用 did    │
│ 生命周期 │      │             │      │ 生命周期    │
└─────────┘      └─────────────┘      └─────────────┘

4.3 Commit 阶段详细流程

阶段一:Before Mutation

javascript 复制代码
// 执行 getSnapshotBeforeUpdate
// 可以在 DOM 变更前获取一些信息(如滚动位置)

阶段二:Mutation(核心变更阶段)

javascript 复制代码
function commitMutationEffects(root, renderPriorityLevel) {
  while (nextEffect !== null) {
    const flags = nextEffect.flags;

    // 1. 插入新节点
    if (flags & Placement) {
      commitPlacement(nextEffect);
    }

    // 2. 更新属性
    if (flags & Update) {
      commitWork(nextEffect);
    }

    // 3. 删除节点
    if (flags & Deletion) {
      commitDeletion(nextEffect);
    }

    nextEffect = nextEffect.nextEffect;
  }
}

// 插入节点的实际操作
function commitPlacement(finishedWork) {
  const parentFiber = getHostParentFiber(finishedWork);
  const parent = parentFiber.stateNode;
  const node = finishedWork.stateNode;

  // 真正的 DOM 操作!
  parent.appendChild(node);
}

阶段三:Layout

javascript 复制代码
function commitLayoutEffects(root, finishedWork) {
  while (nextEffect !== null) {
    const flags = nextEffect.flags;

    // 1. 执行 useLayoutEffect
    if (flags & Update) {
      commitHookEffectListMount(Layout, nextEffect);
    }

    // 2. 调用 componentDidMount / componentDidUpdate
    if (flags & Callback) {
      safelyCallComponentDidUpdate(nextEffect);
    }

    // 3. 执行 setState 的回调
    if (flags & Ref) {
      commitAttachRef(nextEffect);
    }

    nextEffect = nextEffect.nextEffect;
  }
}

// 最后执行 useEffect(异步)
scheduleCallback(NormalPriority, () => {
  flushPassiveEffects();
});

五、Hooks 原理 - 魔法背后的秘密

5.1 Hooks 的本质

Hooks 本质上就是函数,它们利用闭包和链表来存储状态。

5.2 Fiber 上的 Hooks 存储

javascript 复制代码
// Fiber 节点中存储 Hooks 的地方
fiber.memoizedState = {
  // 指向第一个 Hook
  memoizedState: value,    // Hook 当前值
  queue: updateQueue,      // 更新队列
  next: nextHook          // 指向下一个 Hook(形成链表)
}

5.3 Hook 的数据结构

javascript 复制代码
interface Hook {
  memoizedState: any;      // 状态值(useState)或 effect 对象(useEffect)
  baseState: any;          // 基础状态(用于处理跳过的更新)
  baseQueue: Update | null; // 基础更新队列
  queue: UpdateQueue | null; // 当前更新队列
  next: Hook | null;       // 下一个 Hook
}

5.4 Hooks 执行流程

scss 复制代码
函数组件执行
     ↓
┌─────────────────┐
│ renderWithHooks │ ← 初始化 Hooks 上下文
└────────┬────────┘
         ↓
   执行 FunctionComponent()
         ↓
┌─────────────────┐
│   useState()    │ ← 或 useEffect/useMemo 等
└────────┬────────┘
         ↓
┌─────────────────┐
│  mountState()   │ ← 首次渲染
│  或 updateState()│ ← 更新渲染
└────────┬────────┘
         ↓
   返回 [state, dispatch]
         ↓
   继续执行组件函数
         ↓
   finishRender()
         ↓
   调度 Effects(useEffect)

5.5 useState 详细实现

javascript 复制代码
// ========== 首次渲染 ==========
function mountState(initialState) {
  // 1. 创建 Hook 对象
  const hook = mountWorkInProgressHook();

  // 2. 初始化状态
  hook.memoizedState = hook.baseState = initialState;

  // 3. 创建更新队列
  const queue = {
    pending: null,        // 待处理的更新
    dispatch: null,       // dispatch 函数
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: initialState,
  };
  hook.queue = queue;

  // 4. 创建 dispatch 函数(绑定当前 fiber 和 queue)
  const dispatch = dispatchSetState.bind(
    null,
    currentlyRenderingFiber,
    queue
  );
  queue.dispatch = dispatch;

  return [hook.memoizedState, dispatch];
}

// ========== 更新渲染 ==========
function updateState(initialState) {
  // 1. 获取或创建 Hook
  const hook = updateWorkInProgressHook();

  // 2. 处理更新队列
  const queue = hook.queue;
  if (queue.pending !== null) {
    // 有需要处理的更新
    const newState = processUpdateQueue(hook, queue);
    hook.memoizedState = newState;
  }

  return [hook.memoizedState, queue.dispatch];
}

// ========== 触发更新 ==========
function dispatchSetState(fiber, queue, action) {
  // 1. 创建更新对象
  const update = {
    lane: requestUpdateLane(fiber),  // 优先级车道
    action: action,                   // 新值或更新函数
    eagerReducer: null,
    eagerState: null,
    next: null,
  };

  // 2. 加入队列
  const pending = queue.pending;
  if (pending === null) {
    update.next = update;  // 循环链表
  } else {
    update.next = pending.next;
    pending.next = update;
  }
  queue.pending = update;

  // 3. 发起调度
  const root = enqueueConcurrentRenderForLane(fiber, lane);
  scheduleUpdateOnFiber(root, fiber, lane);
}

5.6 useEffect 详细实现

javascript 复制代码
// Effect 对象结构
interface Effect {
  tag: HookFlags;           // 标记(Passive, HasEffect 等)
  create: () => (() => void) | void;  // 副作用函数
  destroy: (() => void) | void;       // 清理函数
  deps: Array<any> | null;  // 依赖数组
  next: Effect;            // 下一个 Effect(循环链表)
}

// ========== 首次渲染 ==========
function mountEffect(create, deps) {
  return mountEffectImpl(
    PassiveEffect | HookPassive,  // 标记为 Passive(异步执行)
    HookPassive,
    create,
    deps
  );
}

function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
  const hook = mountWorkInProgressHook();

  // 创建 effect 对象
  const effect: Effect = {
    tag: hookFlags,
    create,
    destroy: undefined,
    deps,
    next: null,
  };

  // 挂载到 Fiber 的 updateQueue
  hook.memoizedState = pushEffect(hookFlags, create, undefined, deps);
}

// ========== 更新渲染 ==========
function updateEffect(create, deps) {
  return updateEffectImpl(
    PassiveEffect | HookPassive,
    HookPassive,
    create,
    deps
  );
}

function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
  const hook = updateWorkInProgressHook();
  const prevEffect = hook.memoizedState;

  // 检查依赖是否变化
  if (areHookInputsEqual(deps, prevEffect.deps)) {
    // 依赖没变,跳过执行
    hook.memoizedState = pushEffect(hookFlags, create, prevEffect.destroy, deps);
    return;
  }

  // 依赖变了,标记需要执行
  hook.memoizedState = pushEffect(
    HookHasEffect | hookFlags,
    create,
    prevEffect.destroy,
    deps
  );
}

// ========== 执行 Effects ==========
function flushPassiveEffects() {
  // 1. 先执行上一次的清理函数(destroy)
  commitHookEffectListUnmount(HookPassive | HookHasEffect);

  // 2. 再执行这一次的副作用函数(create)
  commitHookEffectListMount(HookPassive | HookHasEffect);
}

5.7 Hook 链表的形成

javascript 复制代码
// 每次调用 Hook 都会创建一个新的 Hook 节点
function mountWorkInProgressHook() {
  const hook = {
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null,
  };

  if (workInProgressHook === null) {
    // 第一个 Hook
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // 链接到链表末尾
    workInProgressHook = workInProgressHook.next = hook;
  }

  return workInProgressHook;
}

// Hook 调用顺序形成链表
function MyComponent() {
  const [state1] = useState(0);  // Hook 1 ← memoizedState
  const [state2] = useState(1);  // Hook 2 ← next
  const [state3] = useState(2);  // Hook 3 ← next
  useEffect(() => {}, []);        // Hook 4 ← next
  // ...
}

为什么 Hooks 不能放在 if 语句里? 因为 React 靠调用顺序来匹配 Hook,如果用 if,会导致顺序错乱,Hook 对应关系就乱了!


六、完整流程串联

6.1 首次渲染流程

scss 复制代码
ReactDOM.createRoot(container).render(<App />)
                        ↓
               创建 FiberRoot 和 RootFiber
                        ↓
               updateContainer(element)
                        ↓
               scheduleUpdateOnFiber(fiber)
                        ↓
┌─────────────────────────────────────────────────────┐
│                    Scheduler                         │
│              requestIdleCallback / MessageChannel    │
│                       ↓                              │
│              performConcurrentWorkOnRoot             │
└─────────────────────────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────┐
│                   Reconciler                         │
│         renderRootConcurrent / renderRootSync        │
│                       ↓                              │
│         workLoopConcurrent / workLoopSync            │
│                       ↓                              │
│         performUnitOfWork() 循环                    │
│                       ↓                              │
│         beginWork(): 创建 Fiber 树                   │
│                       ↓                              │
│         completeWork(): 创建 DOM 节点(不插入)       │
│                       ↓                              │
│         构建 effect 链表                             │
└─────────────────────────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────┐
│                    Renderer                          │
│               commitRoot(root)                       │
│                       ↓                              │
│    ┌──────────────────────────────────────────────┐ │
│    │  commitBeforeMutationEffects                 │ │
│    │  - 执行 getSnapshotBeforeUpdate              │ │
│    └──────────────────────────────────────────────┘ │
│                       ↓                              │
│    ┌──────────────────────────────────────────────┐ │
│    │  commitMutationEffects                       │ │
│    │  - 插入 DOM 节点                             │ │
│    │  - 更新 DOM 属性                             │ │
│    └──────────────────────────────────────────────┘ │
│                       ↓                              │
│    ┌──────────────────────────────────────────────┐ │
│    │  commitLayoutEffects                         │ │
│    │  - 执行 useLayoutEffect                      │ │
│    │  - 调用 componentDidMount                    │ │
│    │  - 绑定 ref                                  │ │
│    └──────────────────────────────────────────────┘ │
│                       ↓                              │
│         调度 useEffect(异步执行)                    │
└─────────────────────────────────────────────────────┘
                        ↓
                   页面显示完成!

6.2 状态更新流程

scss 复制代码
用户点击按钮 → setState(newValue)
                        ↓
              dispatchSetState(fiber, queue, action)
                        ↓
              创建 Update 对象,加入队列
                        ↓
              scheduleUpdateOnFiber(fiber)
                        ↓
┌─────────────────────────────────────────────────────┐
│                    Scheduler                         │
│         根据优先级决定是否立即执行或延迟              │
│         高优先级:立即执行(输入、点击)              │
│         低优先级:进入时间切片调度                    │
└─────────────────────────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────┐
│                   Reconciler                         │
│         创建 workInProgress 树(alternate)          │
│                       ↓                              │
│         复用 current 树的可复用节点                  │
│                       ↓                              │
│         beginWork: 对比 props,标记 flags            │
│                       ↓                              │
│         如果有子组件:递归处理                       │
│                       ↓                              │
│         completeWork: 收集副作用                     │
└─────────────────────────────────────────────────────┘
                        ↓
┌─────────────────────────────────────────────────────┐
│                    Renderer                          │
│         commitRoot(同步执行,不可中断)              │
│                       ↓                              │
│         执行 DOM 变更                                │
│                       ↓                              │
│         执行副作用(useLayoutEffect)                │
│                       ↓                              │
│         调度 useEffect(异步)                       │
└─────────────────────────────────────────────────────┘
                        ↓
                   更新完成!

七、图解核心概念

7.1 时间切片

arduino 复制代码
时间 →
├──────┼──────┼──────┼──────┼──────┼──────┼──────┤
│ 帧1  │  帧2 │  帧3 │  帧4 │  帧5 │  帧6 │  帧7 │
│      │      │      │      │      │      │      │
│工作1 │工作2 │工作3 │工作4 │工作5 │工作6 │工作7 │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘

没有 Scheduler(卡顿):
├─────────────────────────────────────────────────┤
│              一次性完成所有工作                    │
│     (浏览器无法渲染,界面卡住)                    │
└─────────────────────────────────────────────────┘

有了 Scheduler(流畅):
├──────┼──────┼──────┼──────┼──────┼──────┼──────┤
│工作1 │ 渲染 │工作2 │ 渲染 │工作3 │ 渲染 │工作4 │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘

7.2 Fiber 链表遍历

css 复制代码
树结构                    遍历顺序
    A                         A
   / \                       ↓
  B   C      ====>           B
 / \   \                     ↓
D   E   F                    D
    ↑                        ↓
    完成 D 后找兄弟           E
                             ↓
    完成 E 后找兄弟           B(回溯)
                             ↓
    完成 B 后找兄弟           C
                             ↓
    完成 C 后找兄弟           F

7.3 Hooks 链表

scss 复制代码
function Component() {
  const [a] = useState(1);  // Hook 0
  const [b] = useState(2);  // Hook 1
  const ref = useRef(3);     // Hook 2
  useEffect(() => {}, []);   // Hook 3
}

Fiber.memoizedState
       ↓
   ┌────────┐    ┌────────┐    ┌────────┐    ┌────────┐
   │ useState│ → │ useState│ → │ useRef  │ → │useEffect│
   │  hook0  │    │  hook1  │    │  hook2  │    │  hook3  │
   └────────┘    └────────┘    └────────┘    └────────┘
   memoizedState:1 memoizedState:2 memoizedState:  memoizedState:
                   {current:3}    effect对象

八、总结

模块 职责 核心特点
Scheduler 调度任务执行时机 时间切片、优先级管理、可中断
Reconciler 计算虚拟 DOM 差异 Fiber 架构、双缓冲、可中断恢复
Renderer 操作真实 DOM 同步执行、分阶段处理
Hooks 函数组件状态管理 链表存储、闭包捕获、按顺序执行

核心设计思想

  1. 可中断的渲染:把大任务拆小,避免阻塞主线程
  2. 双缓冲技术:两棵树交替工作,保证一致性
  3. 优先级调度:重要更新优先执行
  4. 副作用分离:DOM 变更和副作用执行分开处理

这就是 React 能处理复杂界面并保持流畅的秘密!🎉

参考资料

  1. zh-hans.react.dev/learn
  2. react.iamkasong.com/
  3. message163.github.io/react-docs/...
相关推荐
Csvn5 小时前
React 渲染机制详解
react.js
弓.长.6 小时前
ReactNative for OpenHarmony项目鸿蒙化三方库:react-native-masked-view — 遮罩视图组件
react native·react.js·harmonyos
大雷神6 小时前
HarmonyOS APP<玩转React>开源教程十四:进度管理服务
前端·react.js·开源·harmonyos
早點睡3906 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-image-crop-picker
javascript·react native·react.js
早點睡3906 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-image-gallery
javascript·react native·react.js
console.log('npc')7 小时前
git代码冲突reset,如何回退到冲突之前提交之前的版本
javascript·git·react.js
早點睡3907 小时前
ReactNative项目OpenHarmony三方库集成实战:@react-native-community/geolocation
javascript·react native·react.js
im_AMBER7 小时前
前后端对接: ESM配置与React Router
前端·javascript·学习·react.js·性能优化·前端框架·ecmascript
problc7 小时前
OpenClaw 的前端用的React还是Vue?
前端·vue.js·react.js