Vue3 状态管理方案:Pinia 全指南

张开发
2026/4/11 22:18:12 15 分钟阅读

分享文章

Vue3 状态管理方案:Pinia 全指南
Vue3 状态管理方案Pinia 全指南Pinia 是 Vue 官方推荐的下一代状态管理库完全替代 Vuex支持 Vue3 Composition API、TypeScript 友好、轻量灵活。本文从基础使用、核心 API、高级技巧、注意事项、常见坑五个维度全面讲解。一、快速上手安装与基础使用1. 安装方式1Vite/Vue CLI 项目# npmnpminstallpinia# yarnyarnaddpinia# pnpmpnpmaddpinia2CDN 引入scriptsrchttps://unpkg.com/pinia2.1.7/dist/pinia.iife.js/scriptscriptconst{createPinia}Pinia/script2. 初始化 Pinia在main.ts中注册 Piniaimport{createApp}fromvueimport{createPinia}frompiniaimportAppfrom./App.vueconstappcreateApp(App)constpiniacreatePinia()app.use(pinia)// 注册 Piniaapp.mount(#app)3. 创建第一个 StorePinia 中没有modules每个 Store 都是独立的通过defineStore定义1选项式写法类似 Vuex// src/stores/counter.tsimport{defineStore}frompinia// 第一个参数是 Store 的唯一 ID必须全局唯一exportconstuseCounterStoredefineStore(counter,{// 状态返回初始状态的函数state:()({count:0,name:Pinia}),// 计算属性类似 Vue 的 computed自动缓存getters:{doubleCount:(state)state.count*2,// 访问其他 getters 用 this需指定返回值类型doubleCountPlusOne():number{returnthis.doubleCount1}},// 动作支持同步/异步修改状态的唯一推荐方式actions:{increment(){this.count},asyncfetchData(){constresawaitfetch(/api/data)constdataawaitres.json()this.namedata.name}}})2组合式写法Vue3 Composition API更灵活适合复杂场景// src/stores/user.tsimport{defineStore}frompiniaimport{ref,computed}fromvueexportconstuseUserStoredefineStore(user,(){// 状态用 ref/reactive 定义constuserInforef({name:,age:0})consttokenref()// 计算属性用 computed 替代 gettersconstisAdultcomputed(()userInfo.value.age18)// 动作普通函数支持同步/异步constupdateUser(info:{name:string;age:number}){userInfo.valueinfo}constloginasync(username:string,password:string){constresawaitfetch(/api/login,{method:POST,body:JSON.stringify({username,password})})constdataawaitres.json()token.valuedata.token}// 返回需要暴露的属性和方法return{userInfo,token,isAdult,updateUser,login}})二、核心 API 详解1. Store 实例属性API作用示例$state获取/替换整个状态对象store.$state { count: 10 }$patch批量修改状态推荐store.$patch({ count: store.count 1 })或函数式store.$patch(state state.count)$reset重置状态到初始值仅选项式写法支持组合式需手动实现store.$reset()$subscribe监听状态变化store.$subscribe((mutation, state) console.log(状态变化, mutation))$onAction监听 actions 调用store.$onAction(({ name, args, after, onError }) {})2. Getters 特性缓存机制只有依赖的状态变化时才重新计算访问其他 Store在 getters 中可以调用其他 Store 实例import{useCounterStore}from./countergetters:{crossStoreCount(state){constcounterStoreuseCounterStore()returnstate.countcounterStore.count}}传递参数getters 返回函数实现参数传递此时不会缓存结果getters:{getCountByMultiplier:(state)(multiplier:number)state.count*multiplier}// 组件中调用store.getCountByMultiplier(3)3. Actions 特性支持异步直接在 actions 中使用async/await修改状态可直接修改this.count或用$patch访问其他 Store同 getters直接在 actions 中调用其他 Store错误处理可通过try/catch或$onAction监听错误。三、组件中使用 Store1. Setup 语法糖推荐template div{{ count }}/div button clickincrement1/button /template script setup langts import { useCounterStore } from /stores/counter import { storeToRefs } from pinia // 获取 Store 实例 const counterStore useCounterStore() // 直接访问状态/动作 const increment () counterStore.increment() // 解构状态时必须用 storeToRefs 保持响应式 const { count, doubleCount } storeToRefs(counterStore) /script2. 选项式 API 兼容适合老项目迁移script langts import { useCounterStore } from /stores/counter import { mapState, mapActions, mapGetters } from pinia export default { computed: { ...mapState(useCounterStore, [count, name]), ...mapGetters(useCounterStore, [doubleCount]) }, methods: { ...mapActions(useCounterStore, [increment]) }, mounted() { this.increment() } } /script四、高级技巧1. Store 持久化插件使用pinia-plugin-persistedstate实现状态持久化到 localStorage/sessionStorage安装npminstallpinia-plugin-persistedstate配置// main.tsimport{createPinia}frompiniaimportpersistedstatefrompinia-plugin-persistedstateconstpiniacreatePinia()pinia.use(persistedstate)在 Store 中启用// 选项式写法exportconstuseCounterStoredefineStore(counter,{state:()({count:0}),persist:true// 全局默认配置localStorage// 自定义配置// persist: {// key: counter-store,// storage: sessionStorage,// paths: [count] // 仅持久化count字段// }})// 组合式写法exportconstuseUserStoredefineStore(user,(){constuserInforef({})return{userInfo}},{persist:true})2. Store 模块化拆分按业务模块拆分 Store如user.ts、cart.ts、setting.ts通过目录结构管理src/stores/ ├── index.ts # 导出所有Store ├── user.ts # 用户模块 ├── cart.ts # 购物车模块 └── setting.ts # 设置模块index.ts统一导出export*from./userexport*from./cartexport*from./setting3. Pinia 插件开发自定义插件扩展 Store 功能比如添加日志、全局状态监听// src/plugins/pinia-logger.tsexportconstpiniaLogger({store}){store.$subscribe((mutation,state){console.log([Pinia Logger]${mutation.storeId}状态变化:,mutation.payload,state)})store.$onAction(({name,args,after,onError}){console.log([Pinia Logger] 调用动作:${name}, 参数:,args)after((result){console.log([Pinia Logger] 动作完成:${name}, 返回值:,result)})onError((error){console.error([Pinia Logger] 动作失败:${name}, 错误:,error)})})}// main.ts 注册插件pinia.use(piniaLogger)五、注意事项1. 响应式处理禁止直接解构 Store直接解构会丢失响应式必须用storeToRefs修改状态的推荐方式优先用actions其次用$patch不推荐直接修改store.count 10虽然语法允许但不利于调试和维护组合式写法中的响应式用ref/reactive定义状态确保响应式。2. TypeScript 支持状态类型自动推断选项式写法中 Pinia 会自动推断state的类型组合式写法需手动定义类型复杂对象建议用接口定义interfaceUserInfo{name:string;age:number;}constuserInforefUserInfo({name:,age:0})Actions 参数类型明确参数类型避免隐式 any。3. 性能优化Getters 缓存合理利用 getters 的缓存特性避免重复计算批量修改状态用$patch批量修改减少响应式更新次数避免在 actions 中执行大量同步计算复杂计算建议放到 getters 或单独的工具函数中。六、开发中常见的坑1. 解构 Store 丢失响应式❌ 错误写法const{count}useCounterStore()// count 是普通值不会响应式更新✅ 正确写法import{storeToRefs}frompiniaconst{count}storeToRefs(useCounterStore())// count 是 ref保持响应式2. 组合式写法中无法使用$reset选项式写法的$reset是自动生成的组合式写法需手动实现exportconstuseCounterStoredefineStore(counter,(){constcountref(0)// 手动实现重置函数constreset(){count.value0}return{count,reset}})3. 持久化插件不生效确保插件已正确注册到 Pinia组合式写法中必须在defineStore的第三个参数中配置persist: true如果状态包含复杂对象如 Date、Map需配置serializer自定义序列化persist:{serializer:{serialize:(value)JSON.stringify(value,(key,val){if(valinstanceofDate)returnval.toISOString()returnval}),deserialize:(value)JSON.parse(value,(key,val){if(keycreateTime)returnnewDate(val)returnval})}}4. 在组件外使用 Store 实例❌ 错误写法组件外直接调用 Store可能导致 Pinia 未初始化// src/utils/api.tsimport{useUserStore}from/stores/userconstuserStoreuseUserStore()// 组件外调用可能报错asyncfunctionfetchData(){consttokenuserStore.token}✅ 正确写法在函数内部调用 StoreasyncfunctionfetchData(){constuserStoreuseUserStore()// 函数内部调用确保 Pinia 已初始化consttokenuserStore.token}5. Getters 中使用this丢失类型选项式写法中getters 用箭头函数会丢失this类型需用普通函数❌ 错误写法getters:{doubleCount:()this.count*2// this 类型为 any}✅ 正确写法getters:{doubleCount():number{// 必须指定返回值类型returnthis.count*2}}七、Pinia vs Vuex 对比特性PiniaVuex模块化每个 Store 都是独立模块无嵌套需嵌套 modules结构复杂Mutations无直接在 actions 中修改状态必须通过 mutations 修改状态TypeScript天生支持自动推断类型需手动定义大量类型繁琐体积轻量约 1KB相对笨重DevTools支持 Vue3 DevTools调试更友好对 Vue3 支持有限Pinia 完全替代 Vuex是 Vue3 状态管理的首选方案

更多文章