pinia 在 ssr 中通过每次请求创建独立实例避免状态污染,并序列化至 window.__initial_state__ 供客户端还原,确保服务端与客户端状态同步。

Pinia 在服务端渲染(SSR)中能正确初始化 Store,关键在于避免跨请求状态污染,并确保客户端与服务端状态同步。它不像简单 reactive 单例那样在 Node.js 环境中被复用,而是通过每个请求独立创建 Pinia 实例来隔离状态。
服务端:为每次请求创建独立 Pinia 实例
在 SSR 环境(如 Vite SSR、Nuxt 3 或自建 Express + Vue SSR)中,不能全局复用同一个 Pinia 实例。必须在每次 HTTP 请求处理时新建一个:
- 使用 createPinia() 创建新实例,而不是在模块顶层定义单例
- 将该实例注入到当前请求的 Vue 应用上下文中(例如通过
app.use(pinia)) - 确保所有 store(如
useUserStore())都从该请求专属的 Pinia 实例中获取,而非共享实例
序列化状态并注入 HTML
服务端渲染完成后,需将当前请求的 store 状态序列化为纯 JS 对象,并嵌入页面 HTML 的 window.__INITIAL_STATE__ 中,供客户端还原:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 使用 @nuxt/devalue(推荐)或 devalue 库序列化,它比
JSON.stringify更安全,支持 Map、Set、RegExp、函数名等(但不执行函数) - 务必对输出做 XSS 过滤,例如转义
、<code>>、"等字符,或使用escapeHtml工具 - 典型写法:
window.__INITIAL_STATE__ = {{ devalueResult }},放在<script></script>标签内,且位于所有应用 JS 加载前
客户端:恢复状态并跳过重复初始化
客户端启动时,要读取服务端传来的初始状态,并赋值给 Pinia,同时防止 store 被重复执行 setup 逻辑:
- 检查
window.__INITIAL_STATE__是否存在;若存在,调用pinia.state.value = initialState - 注意:不要直接调用
store.$state = ...,应操作pinia.state.value(它是 Ref,响应式源头) - 确保客户端 Pinia 实例与服务端创建方式一致(比如同样用
createPinia()),否则状态无法匹配 - store 内部的
state: () => ({...})函数仍会执行,但 Pinia 会自动用服务端数据覆盖,无需手动干预
框架集成建议(Vite SSR / Nuxt 3)
不同 SSR 框架有封装好的机制,优先使用官方推荐方式:
-
Vite SSR:利用
transformState钩子,在服务端调用devalue(state),客户端自动还原 -
Nuxt 3:开箱支持 Pinia,只需启用
pinia模块,useStore()会自动 hydrate 服务端状态 - 自建 Express + vue-bundle:需手动在 render 函数中提取 state、注入 script 标签、并在 client entry 中 hydrate
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!









