airi 项目实战:用 Pinia HMR 保留 Store 状态的热更新开发指南
本文基于 airi 仓库中随 Agent 技能库提供的 Pinia 参考资料 advanced-hmr.md 展开,讲解 Pinia 热模块替换(Hot Module Replacement, HMR)的完整接入方式:如何在 store 定义后追加 HMR 片段、acceptHMRUpdate 的作用、Vite / Webpack 等打包器的支持差异,以及在 Nuxt 中的注意事项。读完后你可以照搬代码片段,让 airi 这类 Vite + Pinia 的 Vue 应用在开发阶段修改 store 逻辑时无需整页刷新、状态不丢失。
为什么需要 Store 级 HMR
Pinia 支持 HMR,让开发者可以在不重新加载页面的情况下编辑 store,并保留已有的运行时状态。
对 airi 这类状态复杂的桌面/Web 应用来说这一点尤为关键:从源码结构看,仓库中大量核心 store 都是 Setup Store 风格,例如 useBackgroundStore 通过 defineStore('background', () => { ... }) 定义,内部维护了 ref、useLocalStorage 本地持久化、对象 URL 缓存(blobRefs / urlRefs)以及 IndexedDB 加载逻辑。这类 store 一旦刷新页面,诸如已加载的背景图片缓存、选中项等状态都要重新初始化。接入 HMR 后,修改 store 内部逻辑(新增 getter、调整 action 实现)时可以直接热替换模块,避免反复刷新带来的状态重置和交互成本。
接入方式:在 store 定义后追加 HMR 片段
根据 advanced-hmr.md 的说明,做法是在每一个 store 定义之后追加一段条件注册代码。核心 API 有两个:
defineStore:定义 store(Pinia 标准 API);acceptHMRUpdate:来自pinia包,负责把旧模块的运行时状态迁移到新模块实例,返回一个import.meta.hot.accept的处理器。
Options Store 写法
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useAuth = defineStore('auth', {
// store options...
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useAuth, import.meta.hot))
}
Setup Store 写法
airi 仓库实际采用的正是 Setup Store 风格(如 background.ts 中 defineStore('background', () => {...})),对应的 HMR 片段示例如下:
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot))
}
两个示例的差别仅在于 store 定义形式(options 对象 vs setup 函数),HMR 尾部片段完全一致:acceptHMRUpdate(store, import.meta.hot) 会在模块被热替换时,将当前活跃实例中已有的 state 拷贝到新的 store 定义上,并注册该模块为 HMR 边界(bundler boundary),使 Vite 能就地替换而不向上冒泡到整页刷新。
以 background.ts 为例,如果要在 airi 仓库的实际 store 中落地该能力,只需在文件末尾 }) 之后补上 if (import.meta.hot) { ... } 片段,并将 pinia 的 import 从 import { defineStore } from 'pinia' 扩为 import { acceptHMRUpdate, defineStore } from 'pinia'。从源码结构看,仓库当前各 store 文件(如该文件)尚未统一挂载 acceptHMRUpdate 片段,上述资料文档正是为此准备的接入规范。
打包器支持矩阵
原文档列出了三类打包器环境的支持情况:
- Vite:通过
import.meta.hot官方支持。airi 的多个前端应用(stage-web、stage-tamagotchi 等)都基于 Vite 构建,且入口已经在使用import.meta.hot——例如 stage-tamagotchi 渲染进程入口 中就有if (import.meta.hot) { handleHotUpdate(router, ...) }用于路由表热更新,说明import.meta.hot在该代码库中是可用的标准能力,Pinia store 的 HMR 片段可以无缝共存。 - Webpack:使用
import.meta.webpackHot。 - 其他打包器:任何实现了
import.meta.hot规范的 bundler 理论上都可以工作。这也是为什么片段中要用if (import.meta.hot)做存在性判断——在生产构建(HMR 不注入)或 Webpack 环境下该判断可以安全跳过,不会报错。
Nuxt 环境下的注意事项
文档特别指出:在 Nuxt 项目中配合 @pinia/nuxt 使用时,acceptHMRUpdate 会被自动导入(auto-import),但 HMR 片段本身仍需要手动添加到每个 store 之后。也就是说 Nuxt 只省去了 import 声明,热替换注册这一步不会自动完成。airi 仓库的 Web/桌面应用并不使用 Nuxt(stage-tamagotchi 入口 中可以看到显式 createPinia() 并手动 .use(pinia) 的模式),因此这条主要对使用 Nuxt 的读者有意义。
HMR 带来的实际收益
原文档总结了三点收益,结合仓库的 store 复杂度可以逐条印证:
- 编辑 store 逻辑时不丢状态:修改
useBackgroundStore内的计算逻辑时,已加载的背景选项与选中项得以保留,不用重新走一遍 IndexedDB 加载流程; - 随时增删 state、actions、getters:Setup Store 返回对象变化(如给 background.ts 的 return 中新增一个方法)可以在热替换中直接生效;
- 更快的开发迭代:修改 store 后不再等待整页重载,这对 stage-tamagotchi 这类同时挂载 3D 渲染、音频管线的重型应用尤其有价值——刷新意味着 WebGL 上下文与资源重新初始化。
版本与适用前提
- 本技能资料基于 Pinia v3.0.4 生成(见 SKILL.md 中 "The skill is based on Pinia v3.0.4" 的说明);
- airi 仓库当前通过 pnpm catalog 统一声明
pinia: ^4.0.3(见 pnpm-workspace.yaml),各应用以"pinia": "catalog:"引用(如 stage-web/package.json)。acceptHMRUpdate自 Pinia 早期版本即为公开 API,在 v4 中同样可用; - 前提条件:应用入口必须已经
app.use(pinia)完成初始化,且开发服务器为支持 HMR 的环境(Vite dev server 或等效实现);生产构建中import.meta.hot为 undefined,片段自动不生效,无任何运行时开销。
参考文件
| 内容 | 路径 |
|---|---|
| 本文主体参考文档(Setup / Bundler / Nuxt / Benefits) | .agents/skills/pinia/references/advanced-hmr.md |
| Pinia 技能索引(Advanced 分类入口) | .agents/skills/pinia/SKILL.md |
| 仓库实际 Setup Store 示例 | packages/stage-layouts/src/stores/background.ts |
应用入口的 createPinia 与 import.meta.hot 用法 |
apps/stage-tamagotchi/src/renderer/main.ts |
Pinia 版本声明(catalog: ^4.0.3) |
pnpm-workspace.yaml |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00