Vue 项目不要再用 Pinia 了,组合式 API + ref() 才是王道

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

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

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

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


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

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

ts 复制代码
import { defineStore } from 'pinia'

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

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

ts 复制代码
const userStore = useUserStore()
userStore.setToken('abc')

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


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

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

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

你是不是要这样写?

ts 复制代码
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 多模块管理要这样:

ts 复制代码
// user.ts
export const useUserStore = defineStore(...)

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

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

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

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

但你换成组合式 API 呢?

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

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

ts 复制代码
const user = useUser()
user.setToken('abc')

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


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

你以为写了:

ts 复制代码
state: () => ({
  list: [],
  count: 0
})

就响应式了?其实不是。

当你写:

ts 复制代码
this.list.push(123)

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

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

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

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

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


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

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

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

export function useUser() {
  return {
    token,
    info,
    setToken,
    setInfo,
  }
}

用的时候:

ts 复制代码
import { useUser } from '@/composables/user'
const user = useUser()
user.setToken('abc')

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

ts 复制代码
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

你们怎么看🙂

📌 你可以继续看我的系列文章

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax