普通视图

发现新文章,点击刷新页面。
昨天以前首页

StoreKit 知识总结

作者 charmson
2026年5月26日 12:10

一、什么是 StoreKit 配置文件

StoreKit 配置文件(.storekit)是 Apple 提供的本地测试环境配置文件,用于在 Xcode 中模拟 App Store 内购行为,无需连接真实的 App Store 服务器。

支持的产品类型

类型 说明
Consumable 消耗型,如游戏币
Non-Consumable 非消耗型,如解锁功能
Auto-Renewable Subscription 自动续期订阅
Non-Renewing Subscription 非自动续期订阅

创建方式

Xcode → File → New → File → StoreKit Configuration File
Edit Scheme → Run → Options → StoreKit Configuration(指定文件)

二、本地配置 vs 沙盒测试

对比项 StoreKit 配置(本地) 沙盒测试(Sandbox)
需要网络
需要 App Store Connect 配置
速度 更快 较慢
适合阶段 开发早期 上线前验证

三、清除本地购买记录(重置初始态)

方法一:Xcode 菜单(推荐)

Debug → StoreKit → Clear Purchased Products

清除后重新运行 App 即可,无需重启模拟器。

方法二:重置整个模拟器

Xcode → Window → Devices and Simulators
→ 选中模拟器 → Erase All Content and Settings

方法三:代码同步(仅供参考)

#if DEBUG
try await AppStore.sync()
#endif

四、正式上线后的工作原理

.storekit 配置文件只在开发阶段生效,打包上线后自动忽略。

用户点击购买
    ↓
StoreKit 框架(代码不变)
    ↓
请求发往 Apple 生产服务器
    ↓
Apple 处理支付,返回 Transaction
    ↓
App 验证收据 → 解锁内容

三种环境对比

环境 数据来源 购买记录存储
本地开发 StoreKit 配置文件(.storekit) 本地模拟器沙盒
沙盒测试 App Store Connect(测试环境) Apple 服务器(沙盒)
正式上线 App Store Connect(生产环境) Apple 服务器(生产)

关键点

  • .storekit 文件是"假数据源",上线后真实数据全部走 Apple 服务器
  • StoreKit 是框架(API) ,在三种环境下代码本身不变,变的是背后连接的服务器
  • 用户购买记录永久保存在 Apple 账户中,restore purchases 从 Apple 服务器拉取
  • 上线后需要对 Apple 服务器签发的收据进行验证(客户端或服务端)

React Fiber 架构详解

作者 charmson
2026年5月6日 23:18

从问题出发,理解 React 16 重写渲染引擎的底层逻辑


一、背景:旧架构(Stack Reconciler)的痛点

React 15 的渲染流程

在 React 15 及之前,渲染引擎叫做 Stack Reconciler(栈调和器)。它的工作方式类比递归调用栈:

render()
  └─ diff 子树 A
       └─ diff 子树 B
            └─ diff 子树 C(深度优先,一口气跑完)

整个 Virtual DOM diff + DOM 更新过程是同步、不可中断的。

核心问题:长任务阻塞主线程

浏览器的主线程是单线程的,它需要同时负责:

任务类型 典型时间预算
JS 执行
样式计算
布局 (Layout)
绘制 (Paint)
用户交互响应 必须 ≤ 16ms(60fps)

当组件树很深、更新量很大时,Stack Reconciler 一次 diff 可能耗时 50ms、100ms 甚至更长。在此期间:

  • 🚫 用户点击无响应
  • 🚫 动画卡帧、掉帧
  • 🚫 输入框输入延迟

这就是著名的 "掉帧"(Jank) 问题。

根本矛盾

Stack Reconciler 无法区分任务的优先级,也无法暂停/恢复工作。它就像一个打电话中途不能挂断的人——无论多重要的事情发生,都得等它说完。


二、解决思路:把"同步长任务"变成"可中断的增量工作"

React 团队受到浏览器 requestIdleCallback API 的启发,提出了核心设计目标:

  1. 可中断(Interruptible) :把渲染工作切分成小单元,每个单元执行完后可以暂停,把控制权还给浏览器
  2. 可恢复(Resumable) :暂停后能从中断点继续
  3. 可丢弃(Cancellable) :低优先级的更新可以被高优先级更新抢占,旧工作直接丢弃重来
  4. 优先级调度(Priority Scheduling) :不同类型的更新(用户输入 vs 数据拉取)有不同优先级

这套新架构就是 Fiber


三、Fiber 是什么?

3.1 Fiber 作为数据结构

Fiber 节点是一个普通的 JS 对象,代表组件树中的一个工作单元。每个 React 元素(组件/DOM 节点)都对应一个 Fiber 节点。

// 简化的 Fiber 节点结构
{
  // 组件信息
  type: MyComponent,       // 组件类型
  key: null,
  stateNode: instance,     // 对应的真实 DOM 或组件实例

  // 树结构(链表,而非树)
  return: parentFiber,     // 父节点
  child: firstChildFiber,  // 第一个子节点
  sibling: nextSibling,    // 下一个兄弟节点

  // 工作信息
  pendingProps: {},        // 即将处理的 props
  memoizedProps: {},       // 上次渲染的 props
  memoizedState: {},       // 上次渲染的 state
  updateQueue: [],         // 待处理的更新队列

  // 调度信息
  lanes: Lanes,            // 优先级(Lane 模型)
  flags: Flags,            // 副作用标记(需要插入/更新/删除)

  // 双缓冲
  alternate: fiber,        // 指向另一棵树的对应节点
}

关键点:Fiber 把树结构改成了链表(return/child/sibling 三指针),这使得遍历可以在任意节点暂停,并且可以通过保存当前 Fiber 指针来恢复。

3.2 Fiber 作为工作调度机制

Fiber 架构把渲染分为两个阶段:

┌─────────────────────────────────────────────────────┐
│              Render Phase(可中断)                   │
│                                                     │
│  beginWork → completeWork → beginWork → ...         │
│                                                     │
│  • 纯计算,无副作用                                    │
│  • 构建 workInProgress 树                            │
│  • 可被高优先级任务打断                                │
└─────────────────────────────────────────────────────┘
                         ↓ commit
┌─────────────────────────────────────────────────────┐
│              Commit Phase(不可中断)                  │
│                                                     │
│  before mutation → mutation → layout               │
│                                                     │
│  • 真实 DOM 操作                                     │
│  • 执行副作用(useEffect、生命周期)                    │
│  • 必须一次性完成,保证 UI 一致性                       │
└─────────────────────────────────────────────────────┘

四、双缓冲树(Double Buffering)

Fiber 维护两棵树:

current 树(屏幕上正在显示的)
    ↕ alternate 指针互相指向
workInProgress 树(正在构建的下一帧)
  • current 树:对应当前屏幕渲染的内容
  • workInProgress 树:在 Render Phase 中悄悄构建,用户看不到

当 workInProgress 树构建完成并 commit 后,两棵树直接交换身份(指针切换,O(1) 操作),新树变成 current 树。

这就像 GPU 的双缓冲渲染,避免用户看到中间状态。


五、优先级调度:Lane 模型

React 18 引入了 Lane(车道)模型 来表示优先级(React 16/17 用的是 expirationTime,后被替代)。

Lane 用二进制位表示,支持批量操作:

SyncLane           = 0b0000000000000000000000000000001  ← 最高优先级
InputContinuousLane= 0b0000000000000000000000000000100  ← 用户输入
DefaultLane        = 0b0000000000000000000000000010000  ← 普通更新
TransitionLane     = 0b0000000000000000000001000000000  ← startTransition
IdleLane           = 0b0100000000000000000000000000000  ← 最低优先级

典型场景:用户正在输入(高优先级),同时有数据请求回来触发列表更新(低优先级):

  1. 列表更新开始渲染(workInProgress 树构建中)
  2. 用户输入事件到来 → 优先级更高
  3. React 打断列表更新,优先处理输入 → 输入框立即响应
  4. 输入处理完毕 → 从头重新渲染列表(之前的 workInProgress 树丢弃)

六、Scheduler:时间切片的实现

React 内部有一个独立的调度器 Scheduler,核心思路:

// 伪代码:workLoop 的时间切片
function workLoop(deadline) {
  while (nextUnitOfWork && !shouldYield()) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
  }

  if (nextUnitOfWork) {
    // 还有工作没做完,让出主线程,下一帧继续
    requestIdleCallback(workLoop); // 实际用 MessageChannel 模拟
  } else {
    // 全部做完,进入 Commit Phase
    commitRoot();
  }
}

function shouldYield() {
  return performance.now() >= deadline; // 超过时间片(~5ms)就让出
}

React 没有直接用 requestIdleCallback,因为它在部分浏览器兼容性差、触发频率不可控。实际使用 MessageChannel 模拟,每帧约 5ms 时间片。


七、Fiber 带来的能力全景

能力 依赖机制 代表 API
时间切片 workLoop + shouldYield 默认开启
并发渲染 Render Phase 可中断 createRoot
优先级调度 Lane 模型 startTransition, useDeferredValue
Suspense 渲染可"挂起"并恢复 <Suspense>, use()
并发特性 多个 workInProgress 树 React 18 Concurrent Mode

八、一句话总结

Fiber 把 React 的渲染从"同步递归调用栈"重构为"基于链表的增量工作单元调度系统",使得渲染工作可中断、可恢复、可按优先级调度,从根本上解决了复杂应用的 UI 卡顿问题,并为并发模式奠定了基础。


参考资料

❌
❌