Pinia 组合式 Store:setup 写法与拆分

Options 风格的 defineStore 仍然可用,但 Vue 3 项目里更常见的是 setup store
这篇想讲清楚几件事:setup store 和写 composable 有什么差别、大项目里怎么按域拆 store、以及复用逻辑时怎么避免「到处 import 乱套」

defineStore 传一个 setup 函数时:

  • 你在里面照常 refcomputedwatch
  • 返回什么,外界就拿到什么(方法、state 一起暴露)。

和纯 useXxx() composable 的差别主要是:

  • Pinia 给这个实例 全局单例生命周期(在应用级缓存);
  • store id、DevTools、插件机制可挂。

所以:

  • 跨路由还要存活的状态 → store;
  • 组件树内传一传就行 → composable 往往够用。

实践里比较稳的拆法:

  • 用户与鉴权(token、profile、权限缓存);
  • 业务域(例如订单、商品目录,随产品边界走);
  • 纯 UI 全局(主题、语言)单独一块。

避免:

  • 一个 globalStore 塞所有字段;
  • 为了「少建文件」把无关 action 堆在一起。

store 之间需要协作时:

  • action 里 useOtherStore() 调用即可;
  • 注意 循环依赖:两个 store 互相顶牛时要抽第三层(服务模块或共享 composable)。

组合式 store 返回的多是 ref/computed,在组件里:

  • 模板里直接用通常没问题;
  • 解构时要用 storeToRefs 保住响应式;
  • 方法可以直接解构,一般仍是稳定引用。

团队规范里写清这一条,能少一半「改了不刷新」的疑惑。

常见模式:

  • 纯逻辑、无全局单例需求 → 抽成 composable,在 store 的 setup 里调用;
  • 需要全局共享 → composable 只被 store 使用,对外仍暴露 store。

这样测试时也可以:

  • 单独测 composable;
  • 再测 store 与组件集成。
  • 拆分按 业务域,文件结构跟着域走;
  • 注意 响应式解构store 间依赖 的边界;
  • 复用优先 composable 打底 + store 挂全局

下一篇讲 插件、持久化 与登录态常见落地。