Vue 项目不要再用 Pinia 了

💥先说结论:除非你团队真的"特别大",否则别再迷信 Pinia

用完 Pinia,最后我自己掏出一套组合式 API 封装后,项目变清爽了、调试变简单了、开发变自由了。

Pinia 确实"像 Vuex 的现代替代品",但它仍然是一种「状态管理框架」,一旦项目复杂,它就成了"另一个 Vuex"。

组合式 API + ref() + 封装逻辑函数,其实就是你早就拥有的"更强 store"。


🧱背景:Pinia 被推荐是因为「轻量 + DevTools 支持 + 自动类型推导」

这是我最早喜欢 Pinia 的理由。你看这写法:

import { defineStore } from 'pinia'

    export const useUserStore = defineStore('user', {
      state: () => ({
        token: '',
        info: null,
      }),
      actions: {
        setToken(token: string) {
          this.token = token
        },
      },
    })

是不是熟悉的感觉?既有 state,又有 actions,还能在组件里这样用:

    const userStore = useUserStore()
    userStore.setToken('abc')

但你写着写着,问题就来了。


😫痛点 1:复杂逻辑封装极难

来个真实案例: 登录成功后,要做三件事:

  1. 存 token
  2. 拉用户信息(异步)
  3. 如果用户是"新用户",还要弹一个 modal

你是不是要这样写?

    actions: {
      async login(payload) {
        const res = await loginApi(payload)
        this.token = res.token
        const userInfo = await fetchUserInfo()
        this.info = userInfo

        if (userInfo.isNew) {
          // 在 store 里开弹窗?
          // 还是 emit 事件?
          // 还是从组件层判断?
        }
      }
    }

你会发现:store 被耦合了 UI 行为

你只想"登录",结果 store 里塞了一堆 UI 流程代码。 最后它变成了另一个"小 app.vue"。


😤痛点 2:模块拆分没有比组合式 API 更清晰

Pinia 多模块管理要这样:

    // user.ts
    export const useUserStore = defineStore(...)

    // cart.ts
    export const useCartStore = defineStore(...)

    // 然后全局注册、统一引入

看似干净,但本质还是 Vuex 那套模块思路,你想拆模块,要考虑:

  • 类型合并
  • 名字冲突
  • state 嵌套结构暴露还是封装?

但你换成组合式 API 呢?

    // user.ts
    export const useUser = () => {
      const token = ref('')
      const info = ref(null)
      const setToken = (t: string) => token.value = t
      return {
        token, info, setToken
      }
    }

是不是就和普通 setup() 一样?用的时候直接:

    const user = useUser()
    user.setToken('abc')

逻辑拆分天然用文件系统组织,而不是人为"store 注册"绑定。


🐛痛点 3:调试复杂,响应性还是靠 guess

你以为写了:

    state: () => ({
      list: [],
      count: 0
    })

就响应式了?其实不是。

当你写:

    this.list.push(123)

你有没有遇到页面没更新 的情况?因为不是用 this.list = [...this.list, 123]

这时你得问自己:为什么我要去想"哪种操作才是响应式"?

组合式 API 根本不用想,直接:

    const list = ref<number[]>([])
    list.value.push(123) // 100% 响应式

还支持解构、传递、深度操作,不用 this,不用猜。


⚙️实战:组合式 API 怎么封装出"类 Store"模块?

你可以像这样封一个 user.ts 模块:

    // user.ts
    import { ref } from 'vue'

    const token = ref('')
    const info = ref(null)

    const setToken = (t: string) => token.value = t
    const setInfo = (i: any) => info.value = i

    exportfunctionuseUser() {
      return {
        token,
        info,
        setToken,
        setInfo,
      }
    }

用的时候:

    import { useUser } from '@/composables/user'
    const user = useUser()
    user.setToken('abc')

要是你怕每次都用一个新实例,也可以加缓存:

    let instance: ReturnType<typeof createUser> | null = null

    function createUser() {
      const token = ref('')
      ...
      return { token, ... }
    }

    export const useUser = () => {
      if (!instance) instance = createUser()
      return instance
    }

这不就是你熟悉的 useXxxStore()

✅什么时候用 Pinia 更合适?

Pinia 并不是没价值,它在以下场景更适用:

  1. 大型团队需要明确 store 组织结构
  2. 需要 SSR 支持的场景(如 Nuxt 3 有原生支持)
  3. 习惯"集中式状态管理"的开发者(类 Redux / Vuex)

如果你有几十个状态模块、多人维护、需要强类型约束和统一 DevTools,那 Pinia 是更稳的方案。


❗但如果你是中小团队、独立开发、组件灵活变更多......

用组合式 API 代替 Pinia,是一个完全自然的进化。

你会得到:更简单的状态定义, 更自由的逻辑组合, 更少的心智负担, 更轻的项目依赖(减少打包体积)

Pinia 有它的价值的,但不是每个 Vue 项目都需要它。

Vue 3 最大的价值,就是组合式 API 足够强,已经能当 Store 了。

所以在未来,我倾向这样的架构:

  • 轻状态 :组合式 + ref() 封装逻辑模块
  • 重状态(例如缓存 / SSR / 跨页):再考虑 Pinia or Vuex

你们怎么看🙂



原文链接:https://juejin.cn/post/7517197769246720012





🔥号外~号外~

最近我们推出了大厂的一手面经模块,都是刚面完的小伙伴们热乎乎分享的:

  • 字节、阿里、腾讯最新面试真题
  • 面试流程和注意事项
  • 面试官的重点提问和考察点

这些面经都是花了不少心思整理的,比网上那些过时的八股文靠谱多了。

有需要的小伙伴可以点击这里👉前端面试题宝典打开小程序,首页即可直接领取【大厂真实面经】),也可直接联系小助理咨询。

毕竟信息差就是竞争力,早点了解面试套路,早点拿到心仪offer!

有会员购买、辅导咨询的小伙伴,可以通过下面的二维码,联系我们的小助手。