← 返回文章列表
Vue·493 次阅读

Pinia 工程化实践:组织、持久化与服务端渲染

Pinia 工程化实践:组织、持久化与服务端渲染

store 的组织方式

按业务领域拆分而不是按技术类型:

stores/
  user.ts        # 登录态、权限、资料
  posts.ts       # 文章列表、筛选状态
  comments.ts
  ui.ts          # 全局 UI:侧边栏开合、主题、通知

原则依然是:组件私有状态别放 store;只有跨路由、跨组件共享的才进。每个 store 保持单一职责,过大就按子领域再拆,store 之间可以互相调用,但要避免循环依赖。

组合式 store 的复用

通用逻辑抽成独立函数,store 里组合——和组件抽 composable 完全一样:

// composables/useAsyncResource.ts
export function useAsyncResource<T>(fetcher: () => Promise<T>) {
  const data = ref<T>()
  const loading = ref(false)
  async function load() {
    loading.value = true
    data.value = await fetcher()
    loading.value = false
  }
  return { data, loading, load }
}

// stores/posts.ts
export const usePostsStore = defineStore('posts', () => {
  const { data: list, loading, load } = useAsyncResource(() =>
    fetch('/api/posts').then(r => r.json())
  )
  const count = computed(() => list.value?.length ?? 0)
  return { list, loading, count, load }
})

状态持久化

刷新页面 Pinia 状态会重置。用户偏好(主题、侧边栏)可直接用 VueUse:

import { useStorage } from '@vueuse/core'
const theme = useStorage('theme', 'light') // 自动同步 localStorage

通用方案用 pinia-plugin-persistedstate,在 store 上声明 persist,自动按 key 存取,还能选择只持久化部分字段。不要持久化敏感数据(token 应走 httpOnly Cookie)。

跨组件/路由的共享动作

// 组件外(如路由守卫、fetch 拦截器)使用 store,必须等 pinia 安装后
router.beforeEach(async () => {
  const user = useUserStore()
  if (!user.checked) await user.fetchMe()
})

SSR 注意点

  • store 必须在每个请求内创建新实例(Nuxt 已自动处理),否则用户间状态串号
  • 服务端填充的数据要在客户端激活前就位,Nuxt 里 useState/useFetch 自动序列化
  • 避免在 store 初始化时直接访问 window/document

调试

DevTools 支持时间旅行、store 热更新;$onAction 可统一做埋点和日志。配合 TS,整个 store 的状态流几乎是自文档化的。

看到这里了?如果这篇文章帮到过你

请我喝瓶可乐就好 🥤

收款码

评论0 条

还没有评论,来抢沙发~

发表评论

评论需审核后显示

© 2026 许冬博客 · 专注 JS 技术分享

·RSS 订阅·Powered by Nuxt 4