文档目录
状态管理机制

简介

本技术文档聚焦 DouPHP 小程序前端的“stores”状态管理方案,围绕认证、购物车与全局公共数据三大领域,系统阐述状态定义、更新、订阅与响应式更新的完整链路。重点解释:

  • 认证状态 authStore:登录态持久化、恢复、降级策略与鉴权流程。
  • 购物车状态 cartStore:数量聚合、角标计算与模块开关控制。
  • 全局协调 index.ts:启动引导、统一绑定与副作用驱动。
  • 公共数据 commonStore:种子数据、网络刷新与本地缓存的 SWR 模式。
  • 持久化 persist.ts:带 schema 版本的存储封装与自动写回。
  • 响应式原理:基于 MobX 的可观察对象、动作与 autorun 驱动的视图同步与性能优化。

项目结构

小程序前端的状态管理集中在 miniprogram/default/stores 目录,采用模块化 store 设计,通过统一的入口进行初始化与副作用绑定。类型定义位于 types/store.d.ts,MobX 运行时由 libs/mobx-miniprogram 提供。

graph TB
subgraph "stores"
A["auth.ts<br/>认证状态"]
B["cart.ts<br/>购物车状态"]
C["common.ts<br/>公共数据"]
D["index.ts<br/>启动与绑定"]
E["persist.ts<br/>持久化封装"]
end
subgraph "types"
T["store.d.ts<br/>状态类型声明"]
end
subgraph "libs"
M["mobx-miniprogram/index.js<br/>可观察/动作/autorun"]
end
D --> A
D --> B
D --> C
C --> E
A --> M
B --> M
C --> M
A -.-> T
B -.-> T
C -.-> T

核心组件

  • 认证状态(authStore):负责登录凭证、用户标识与登录态标志的持久化、恢复与校验;支持可选模块降级。
  • 购物车状态(cartStore):维护购物车数量与角标文案;在未启用订单模块或未登录时短路返回零值。
  • 公共数据(commonStore):承载站点信息、语言包、导航、功能特性等;采用 SWR 模式实现首屏秒开与后台刷新。
  • 启动协调(bootstrapStores):按序执行 hydrate → refresh/restore,并绑定购物车角标的 autorun 副作用。
  • 持久化(persist.ts):以 schema_version 保护缓存版本,提供 load/save/clear 与 autorun 自动写回能力。

架构总览

整体采用“单一真相源 + 可观察状态 + 副作用驱动”的模式:

  • 数据层:commonStore 作为 bootstrap 信封的唯一真相源,authStore 与 cartStore 分别维护各自域的状态。
  • 状态更新:所有变更通过 action 包裹,确保在 MobX 事务中批量提交,避免中间态闪烁。
  • 响应式更新:使用 autorun/computed 将状态变化映射到 UI 或平台 API(如 tabBar 角标)。
  • 启动流程:先 hydrate 从 storage 快速渲染,再并发/串行刷新网络数据,失败不阻断首屏。
sequenceDiagram
participant App as "应用启动"
participant Bootstrap as "bootstrapStores"
participant Common as "commonStore"
participant Auth as "authStore"
participant Cart as "cartStore"
participant UI as "视图/TabBar"
App->>Bootstrap : 调用
Bootstrap->>Common : hydrate()
Bootstrap->>Auth : hydrate()
Bootstrap->>UI : bindCartBadge() (autorun)
Bootstrap->>Common : refresh()
Common-->>Bootstrap : 完成(含 features)
Bootstrap->>Auth : restore()
Bootstrap->>Cart : refresh()
Note over Auth,Cart : 根据 features 与登录态决定是否请求
Cart-->>UI : badge 变化触发 set/remove TabBarBadge

详细组件分析

认证状态(authStore)

  • 状态字段:api_token、user_id、loginEd、is_login、is_vip、is_work、is_distribution。
  • 关键方法:
    • hydrate:从 storage 读取凭证与登录标记,用于冷启动秒开。
    • restore:根据 commonStore.features.user 决定是否拉取 user/index;仅 UNAUTHORIZED 时清空 is_login,其它错误保留当前态,避免弱网假登出。
    • ensureLogin:若未登录则跳转默认登录页。
    • login/logout:写入/清除 storage 并同步状态。
  • 降级策略:当 features.user === false 时,强制 is_login 为 false,且不发起任何请求。
flowchart TD
Start(["进入 restore"]) --> CheckModule{"features.user 是否启用?"}
CheckModule --> |否| SetEmpty["applyAuthFlags({}) 置空标志"] --> End(["结束"])
CheckModule --> |是| Fetch["调用 user/index"]
Fetch --> Resp{"响应码"}
Resp --> |UNAUTHORIZED| Clear["applyAuthFlags({}) 清空标志"] --> End
Resp --> |其他/成功| Apply["applyAuthFlags(后端 flags)"] --> End

购物车状态(cartStore)

  • 状态字段:number(数量)。
  • 派生属性:badge(角标文案),0 显示空串,>99 显示 '99+'。
  • 关键方法:
    • refresh:在未启用 order 模块或未登录时短路返回 0;否则请求 order/cart_number 并更新 number。
    • reset:重置数量为 0。
  • 降级策略:order 模块未启用或未登录时,静默返回 0,不抛错、不弹提示。
flowchart TD
S(["refresh"]) --> Mod{"order 模块已启用?"}
Mod --> |否| ZeroA["number=0"] --> DoneA(["返回"])
Mod --> |是| Token{"存在 api_token?"}
Token --> |否| ZeroB["number=0"] --> DoneB(["返回"])
Token --> |是| Call["请求 order/cart_number"]
Call --> Update["number = 返回值(默认0)"] --> DoneC(["返回"])

全局协调(index.ts)

  • 职责:导出各 store;绑定购物车角标的 autorun;提供 bootstrapStores 启动流程。
  • 启动顺序:
    1. commonStore.hydrate() 与 authStore.hydrate() 立即填充本地缓存数据。
    2. bindCartBadge() 建立 autorun,监听 cartStore.badge 变化并调用 wx.setTabBarBadge / removeTabBarBadge。
    3. commonStore.refresh() 完成后,再调用 authStore.restore() 与 cartStore.refresh(),依据最新 features 决定降级行为。
sequenceDiagram
participant Entry as "入口"
participant BS as "bootstrapStores"
participant CM as "commonStore"
participant AU as "authStore"
participant CT as "cartStore"
participant WX as "wx API"
Entry->>BS : 调用
BS->>CM : hydrate()
BS->>AU : hydrate()
BS->>BS : bindCartBadge()
BS->>CM : refresh()
CM-->>BS : 完成
BS->>AU : restore()
BS->>CT : refresh()
CT-->>WX : set/remove TabBarBadge

公共数据(commonStore)

  • 作用:承载站点配置、语言包、导航、功能特性等;对外暴露 features,供 module-guard、authStore、cartStore 做降级判断。
  • 生命周期:
    • applyData:合并种子数据与网络数据,设置 ready=true。
    • hydrate:从持久化加载上次刷新结果,保证首屏可用。
    • refresh:并行获取 bootstrap 与 lang,合并后写回持久化;失败不影响已有数据。
flowchart TD
H["hydrate"] --> Load["loadPersisted('common')"]
Load --> ApplyH{"有缓存?"}
ApplyH --> |是| UseCache["applyData(缓存)"] --> ReadyH["ready=true"]
ApplyH --> |否| SkipH["跳过"] --> ReadyH
R["refresh"] --> Fetch["fetchBootstrap + fetchLang"]
Fetch --> Merge["合并 payload 与 langPack"]
Merge --> ApplyR["applyData(合并结果)"]
ApplyR --> Save["savePersisted('common', merged)"]

持久化(persist.ts)

  • 设计要点:
    • 以 { __v: SCHEMA_VERSION, data } 结构存储,版本升级自动失效旧缓存。
    • 提供 load/save/clear 工具函数,异常静默处理。
    • 提供 persistAutorun:用 autorun 订阅 selector,变化即写回 storage。
classDiagram
class Persist {
+SCHEMA_VERSION : number
+loadPersisted(key) : any
+savePersisted(key, data) : void
+clearPersisted(key) : void
+persistAutorun(key, selector) : () => void
}

响应式与类型

  • 响应式基础:observable/action/runInAction/autorun 来自 mobx-miniprogram,提供可观察对象、受控修改与副作用订阅。
  • 类型契约:store.d.ts 定义了 CommonState/AuthState/CartState,确保 store 字段名与页面 wxml 绑定一致,形成单一真相源。

依赖关系分析

  • 模块内聚:每个 store 专注单一业务域,通过 commonStore.features 解耦可选模块。
  • 外部依赖:
    • http:用于网络请求(user/index、order/cart_number)。
    • route:动态解析路由键,未注册路由时抛出 Unknown route,被上层 try/catch 捕获并降级。
    • wx API:storage、redirectTo、set/removeTabBarBadge。
  • 耦合点:
    • index.ts 对 cartStore.badge 的 autorun 绑定是跨模块副作用的关键点。
    • authStore 与 cartStore 均依赖 commonStore.features 做降级决策。
graph LR
Index["index.ts"] --> Auth["auth.ts"]
Index --> Cart["cart.ts"]
Index --> Common["common.ts"]
Common --> Persist["persist.ts"]
Auth --> Http["services/http.js"]
Cart --> Http
Auth --> Route["utils/route.js"]
Cart --> Route
Index --> Wx["wx API"]

性能考虑

  • 首屏秒开:commonStore.hydrate 与 authStore.hydrate 优先从 storage 恢复,减少白屏时间。
  • 网络刷新后置:在 hydrate 之后再进行 refresh/restore,失败不阻断首屏渲染。
  • 局部更新:action 包裹状态变更,配合 MobX 细粒度追踪,仅更新依赖该状态的视图节点。
  • 角标绑定去抖:autorun 仅在 badge 变化时调用 wx API,避免频繁 set/remove。
  • 降级短路:order 未启用或未登录时直接返回 0,避免无效请求。

故障排查指南

  • 登录后仍显示未登录:
    • 检查 ensureLogin 与 restore 的 UNAUTHORIZED 分支是否正确清空 is_login。
    • 确认 http 拦截器是否正确携带 Authorization: Bearer 并处理会话过期。
    • 参考路径:miniprogram/default/stores/auth.ts:91-115
  • 购物车角标不更新:
    • 确认 bindCartBadge 已执行且 resolveTabIndex 能正确解析购物车 tab 索引。
    • 检查 cartStore.refresh 是否因 features.order=false 或未登录而短路。
    • 参考路径:miniprogram/default/stores/index.ts:20-42、miniprogram/default/stores/cart.ts:54-72
  • 首屏数据为空:
    • 检查 commonStore.hydrate 是否能从 persist 加载上次缓存。
    • 确认 refresh 失败时不会覆盖已有数据。
    • 参考路径:miniprogram/default/stores/common.ts:74-106
  • 版本升级后缓存失效:
    • 确认 persist.ts 的 SCHEMA_VERSION 已递增,旧缓存将被丢弃。
    • 参考路径:miniprogram/default/stores/persist.ts:9-34

结论

DouPHP 小程序的状态管理以 MobX 为核心,采用“可观察状态 + 动作 + 副作用”的清晰分层:

  • commonStore 提供统一的数据真相源与模块开关;
  • authStore 与 cartStore 分别管理认证与购物车域,具备完善的降级与容错;
  • index.ts 集中编排启动流程与 UI 副作用;
  • persist.ts 保障缓存版本安全与自动写回。 该方案在保证首屏体验的同时,提供了可扩展、可维护的状态管理基础设施。

附录

  • 最佳实践
    • 始终通过 action 修改状态,避免绕过 MobX 追踪。
    • 使用 computed/autorun 表达派生状态与副作用,保持逻辑与视图解耦。
    • 利用 features 控制可选模块,确保未安装模块时的稳定降级。
    • 对网络请求失败保持幂等与静默兜底,避免影响用户体验。
    • 使用 persist 的版本机制管理数据结构演进。
  • 常见问题
    • 路由未注册导致 route() 抛错:在上层 try/catch 中捕获并降级。
    • 非 tabBar 页面调用 setTabBarBadge 失败:在 catch 中静默忽略。
    • 弱网导致误判未登录:仅在 UNAUTHORIZED 时清空 is_login。
添加日期:2026-10-05