别再纠结Vuex和Pinia了!手把手教你用Pinia重构一个TodoList(附TypeScript支持)
从Vuex到Pinia现代化状态管理的实战迁移指南在Vue生态系统中状态管理一直是构建复杂应用的关键环节。随着Vue 3的普及和Composition API的成熟Pinia作为新一代状态管理方案正在快速取代Vuex的地位。本文将从一个实际开发者的角度分享如何将现有Vuex项目平滑迁移到Pinia并通过TodoList案例展示其核心优势。1. 为什么选择Pinia超越Vuex的五大理由性能优化是Pinia最显著的优势之一。与Vuex相比Pinia的打包体积缩小了约40%在小型应用中可能只有1.6KB左右。这种轻量级特性来自于几个关键设计决策移除了Vuex中复杂的mutations概念采用扁平化的store结构替代嵌套模块基于Vue 3的响应式系统重构核心逻辑// Pinia的典型store定义 import { defineStore } from pinia export const useTodoStore defineStore(todos, { state: () ({ items: [] as TodoItem[] }), getters: { completedCount: (state) state.items.filter(i i.done).length }, actions: { addTodo(text: string) { this.items.push({ id: Date.now(), text, done: false }) } } })TypeScript支持是另一个决定性因素。Pinia从底层开始就采用TypeScript编写提供了开箱即用的类型推断。相比之下Vuex需要复杂的类型声明才能获得类似的开发体验。提示Pinia的actions可以直接修改state无需像Vuex那样区分mutations和actions这显著减少了样板代码。2. 迁移路线图从Vuex到Pinia的渐进式策略对于已有Vuex项目我们推荐采用渐进式迁移策略。以下是一个可行的步骤清单并行运行阶段在项目中同时安装Pinia和Vuex新功能优先新开发的功能模块直接使用Pinia实现逐步替换按业务模块逐个迁移现有Vuex store最终清理当所有模块迁移完成后移除Vuex依赖迁移过程中需要特别注意两者的关键差异点特性VuexPinia状态定义单一store多个独立store修改方式mutations/actions直接修改或actions模块化嵌套模块扁平化storeTypeScript需要额外配置原生支持代码组织集中式组合式API风格3. TodoList实战Pinia的最佳实践让我们通过一个完整的TodoList实现来展示Pinia的实际应用。首先创建核心store// stores/todo.ts interface TodoItem { id: number text: string done: boolean } export const useTodoStore defineStore(todos, { state: () ({ items: [] as TodoItem[], filter: all as all | active | completed }), getters: { filteredItems(state) { switch (state.filter) { case active: return state.items.filter(item !item.done) case completed: return state.items.filter(item item.done) default: return state.items } }, stats() { const total this.items.length const completed this.items.filter(i i.done).length return { total, completed, active: total - completed } } }, actions: { addTodo(text: string) { if (!text.trim()) return this.items.push({ id: Date.now(), text, done: false }) }, toggleTodo(id: number) { const item this.items.find(i i.id id) if (item) item.done !item.done }, removeTodo(id: number) { this.items this.items.filter(i i.id ! id) } } })在组件中使用这个store变得异常简单script setup langts import { useTodoStore } from /stores/todo const todoStore useTodoStore() const newTodo ref() function handleAdd() { todoStore.addTodo(newTodo.value) newTodo.value } /script template div classtodo-app input v-modelnewTodo keyup.enterhandleAdd ul li v-foritem in todoStore.filteredItems :keyitem.id input typecheckbox v-modelitem.done span :class{ done: item.done }{{ item.text }}/span button clicktodoStore.removeTodo(item.id)×/button /li /ul div classstats 总计: {{ todoStore.stats.total }} | 已完成: {{ todoStore.stats.completed }} | 进行中: {{ todoStore.stats.active }} /div /div /template4. 高级技巧提升Pinia开发体验持久化存储是实际项目中常见需求。通过pinia-plugin-persistedstate可以轻松实现import { createPinia } from pinia import piniaPluginPersistedstate from pinia-plugin-persistedstate const pinia createPinia() pinia.use(piniaPluginPersistedstate) // 在store定义中启用 export const useTodoStore defineStore(todos, { persist: true, // ...其他配置 })单元测试策略也变得更加直观。由于Pinia store本质上是普通JavaScript对象测试时无需复杂的mockimport { setActivePinia, createPinia } from pinia import { useTodoStore } from /stores/todo describe(Todo Store, () { beforeEach(() { setActivePinia(createPinia()) }) it(should add new todo, () { const store useTodoStore() store.addTodo(Test todo) expect(store.items).toHaveLength(1) expect(store.items[0].text).toBe(Test todo) }) })对于大型项目store组合是保持代码整洁的有效方式。我们可以将大型store拆分为多个逻辑单元// stores/todo/items.ts export const useTodoItems defineStore(todoItems, { state: () ({ items: [] as TodoItem[] }), actions: { add(text: string) { /*...*/ }, remove(id: number) { /*...*/ } } }) // stores/todo/filter.ts export const useTodoFilter defineStore(todoFilter, { state: () ({ filter: all }), getters: { filteredItems() { /*...*/ } } }) // 在组件中使用 const itemsStore useTodoItems() const filterStore useTodoFilter()Pinia的DevTools集成提供了出色的调试体验。在Vue DevTools中你可以实时查看所有store的状态跟踪状态变化历史直接触发actions进行测试迁移到Pinia不仅是技术栈的更新更是开发思维的转变。它代表了Vue生态向更简单、更符合现代前端工程实践的方向演进。在实际项目中这种转变通常会带来20-30%的代码量减少和显著的类型安全提升。