Pinia vs Vuex:为什么我全面迁移到了 Pinia
用了两年 Vuex,迁移到 Pinia 之后最大的感受:之前写的很多代码都是在和框架搏斗。
Vuex 的痛点
TypeScript 支持差
1 2 3 4 5
| const store = useStore()
|
模块嵌套地狱
1 2 3 4 5
| store.state.workOrder.list store.getters['workOrder/filteredList'] store.commit('workOrder/SET_FILTER', {}) store.dispatch('workOrder/fetchList')
|
字符串路径,重构时噩梦。
Mutation 纯属多余
Vuex 强制「Mutation → Action」两步走,但大部分场景 Mutation 只是赋值:
1 2 3 4 5 6 7 8 9 10
| mutations: { SET_LIST(state, list) { state.list = list } }, actions: { async fetchList({ commit }) { const list = await api.getList() commit('SET_LIST', list) } }
|
Pinia 的优势
完美的 TypeScript 支持
1 2 3 4 5
| const store = useWorkOrderStore() store.list store.filteredList store.fetchList()
|
Setup Store 写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| export const useWorkOrderStore = defineStore('workOrder', () => { const list = ref<WorkOrder[]>([]) const filter = ref({ status: '', keyword: '' })
const filteredList = computed(() => list.value.filter(item => { if (filter.value.status && item.status !== filter.value.status) return false if (filter.value.keyword && !item.title.includes(filter.value.keyword)) return false return true }) )
async function fetchList() { list.value = await api.getWorkOrders() }
function setFilter(f: typeof filter.value) { filter.value = f }
return { list, filter, filteredList, fetchList, setFilter } })
|
没有 Mutation,没有 Commit,直接操作 ref。
代码对比
| 特性 |
Vuex |
Pinia |
| TypeScript |
手动定义类型 |
自动推导 |
| 嵌套模块 |
state.a.b.c |
store.c(扁平化) |
| 代码量 |
Mutation + Action + Getter |
只有 ref + computed + 函数 |
| DevTools |
支持 |
支持 |
| SSR |
支持 |
支持 |
| 包体积 |
~10KB |
~1.5KB |
迁移策略
不需要一次性迁移,Pinia 和 Vuex 可以共存:
1 2 3 4 5
| const useNewStore = defineStore('new', () => { ... })
const store = useStore()
|
逐步把 Vuex 的 store 迁移为 Pinia,直到全部替换后移除 Vuex。
总结
Pinia 不是 Vuex 的替代品,它是 Vuex 早就应该有的样子。如果你还在用 Vuex,现在就是迁移的最佳时机。