Pinia 是 Vue3 官方推荐的状态管理库(替代 Vuex),相比 Vuex 更简洁、轻量化,原生支持组合式 API,无嵌套模块限制,且完美兼容 Vue DevTools。下面时pinia官方的介绍:
下面介绍一下 Pinia这款状态管理工具的完整用法,实际项目在的大多数场景可以直接照着代码改一下就可以用了。
一、核心优势(为什么选 Pinia)
- 无需嵌套模块,天然支持「扁平化模块化」;
- 抛弃
mutation(仅需state/getters/actions),简化状态修改逻辑; - 原生支持 TypeScript,类型提示更友好;
- 支持组合式 API(
setup)和选项式 API 两种写法; - 体积更小(约 1KB),性能更优;
- 完美兼容 Vue DevTools(支持时间旅行、状态追踪)。
二、基础安装
# npmnpminstallpinia# yarnyarnaddpinia# pnpmpnpmaddpinia三、全局挂载 Pinia 实例
在main.js中创建并挂载 Pinia 实例(所有组件可共享 Store):
// src/main.js (Vue3 + Vite)import{createApp}from'vue';import{createPinia}from'pinia';// 导入 PiniaimportAppfrom'./App.vue';constapp=createApp(App);constpinia=createPinia();// 创建 Pinia 实例app.use(pinia);// 挂载到 Vue 应用app.mount('#app');四、定义 Store(核心)
Pinia 中「Store」是状态管理的核心单元,每个业务模块(如用户、购物车)对应一个独立的 Store。
支持两种写法:选项式 API(类似 Vuex)和组合式 API(更灵活)。
1. 选项式 API 写法(推荐新手)
// src/stores/counterStore.jsimport{defineStore}from'pinia';// 第一个参数:Store 唯一标识(必须唯一),第二个参数:配置对象exportconstuseCounterStore=defineStore('counter',{// 1. 状态:返回初始值的函数(类似 Vue 的 data)state:()=>({count:0,title:'Pinia 计数器',userInfo:{name:'张三',age:20}}),// 2. 计算属性:类似 Vue 的 computed,缓存结果getters:{// 基础用法:基于 state 计算doubleCount:(state)=>state.count*2,// 依赖其他 getter(通过 this 访问)doubleCountPlusOne(){returnthis.doubleCount+1;},// 带参数的 getter(返回函数)getCountByMultiplier:(state)=>(multiplier)=>{returnstate.count*multiplier;}},// 3. 方法:支持同步/异步,修改状态(类似 Vue 的 methods)actions:{// 同步修改increment(){this.count++;// 直接修改 state,无需 mutation},// 带参数的同步方法incrementBy(num){this.count+=num;},// 异步方法(如接口请求)asyncfetchData(){constres=awaitfetch('https://api.example.com/count');constdata=awaitres.json();this.count=data.count;// 异步修改状态},// 调用其他 actionasyncfetchAndIncrement(){awaitthis.fetchData();// 调用自身异步 actionthis.increment();// 调用同步 action}}});2. 组合式 API 写法(推荐 Vue3 组合式项目)
更灵活,可复用组合式函数(composables):
// src/stores/counterStore.jsimport{defineStore}from'pinia';import{ref,computed}from'vue';// 组合式 API 写法:返回函数exportconstuseCounterStore=defineStore('counter',()=>{// 1. 状态:等价于 state(用 ref/reactive 定义)constcount=ref(0);consttitle=ref('Pinia 计数器');constuserInfo=ref({name:'张三',age:20});// 2. 计算属性:等价于 getters(用 computed 定义)constdoubleCount=computed(()=>count.value*2);constdoubleCountPlusOne=computed(()=>doubleCount.value+1);constgetCountByMultiplier=(multiplier)=>count.value*multiplier;// 3. 方法:等价于 actions(普通函数)constincrement=()=>{count.value++;};constincrementBy=(num)=>{count.value+=num;};constfetchData=async()=>{constres=awaitfetch('https://api.example.com/count');constdata=awaitres.json();count.value=data.count;};// 必须返回需要暴露的状态/方法return{count,title,userInfo,doubleCount,doubleCountPlusOne,getCountByMultiplier,increment,incrementBy,fetchData};});五、组件中使用 Store
1. 基础使用(组合式 API 组件)
<!-- src/components/Counter.vue --> <template> <div> <h3>{{ counterStore.title }}</h3> <p>当前计数:{{ counterStore.count }}</p> <p>双倍计数:{{ counterStore.doubleCount }}</p> <p>三倍计数:{{ counterStore.getCountByMultiplier(3) }}</p> <button @click="counterStore.increment">+1</button> <button @click="counterStore.incrementBy(5)">+5</button> <button @click="counterStore.fetchData">异步获取计数</button> <button @click="resetCount">重置计数</button> </div> </template> <script setup> // 1. 导入 Store 函数 import { useCounterStore } from '@/stores/counterStore'; // 2. 创建 Store 实例(Pinia 会自动复用单例,多次调用也返回同一个实例) const counterStore = useCounterStore(); // 3. 直接修改状态(Pinia 默认允许,可通过严格模式禁止) const resetCount = () => { counterStore.count = 0; }; </script>2. 解构 Store 保持响应式(关键)
直接解构 Store 会丢失响应式,需用storeToRefs辅助:
<script setup> import { useCounterStore } from '@/stores/counterStore'; import { storeToRefs } from 'pinia'; // 导入辅助函数 const counterStore = useCounterStore(); // 错误:解构后丢失响应式 // const { count, doubleCount } = counterStore; // 正确:用 storeToRefs 解构,保留响应式 const { count, doubleCount, title } = storeToRefs(counterStore); // actions 方法无需解构(本身是函数,不涉及响应式) const { increment, fetchData } = counterStore; </script> <template> <div> <p>{{ title }}</p> <p>{{ count }}</p> <p>{{ doubleCount }}</p> <button @click="increment">+1</button> <button @click="fetchData">异步获取</button> </div> </template>3. 选项式 API 组件中使用
<script> import { useCounterStore } from '@/stores/counterStore'; import { mapState, mapActions } from 'pinia'; // 导入映射辅助函数 export default { computed: { // 映射 state/getters 到计算属性 ...mapState(useCounterStore, ['count', 'doubleCount', 'title']) }, methods: { // 映射 actions 到方法 ...mapActions(useCounterStore, ['increment', 'fetchData']) }, mounted() { console.log(this.count); // 访问状态 this.increment(); // 调用方法 } }; </script>六、模块化(多 Store 管理)
Pinia 天然支持模块化,每个业务模块对应一个独立的 Store,无需嵌套:
src/stores/ ├── counterStore.js // 计数器 Store ├── userStore.js // 用户 Store ├── cartStore.js // 购物车 Store示例:用户 Store
// src/stores/userStore.jsimport{defineStore}from'pinia';exportconstuseUserStore=defineStore('user',{state:()=>({token:'',userInfo:null,permissions:[]}),actions:{// 登录asynclogin(username,password){constres=awaitfetch('/api/login',{method:'POST',body:JSON.stringify({username,password})});constdata=awaitres.json();this.token=data.token;this.userInfo=data.userInfo;this.permissions=data.permissions;},// 退出登录logout(){this.token='';this.userInfo=null;this.permissions=[];}}});组件中按需导入多 Store
<script setup> import { useCounterStore } from '@/stores/counterStore'; import { useUserStore } from '@/stores/userStore'; const counterStore = useCounterStore(); const userStore = useUserStore(); // 调用不同 Store 的方法 const handleLogin = async () => { await userStore.login('admin', '123456'); counterStore.increment(); }; </script>七、进阶用法
1. 状态持久化(本地存储)
安装插件pinia-plugin-persistedstate,可以实现 Store 状态自动持久化到localStorage/sessionStorage:
npminstallpinia-plugin-persistedstate// src/main.jsimport{createApp}from'vue';import{createPinia}from'pinia';importpiniaPluginPersistedstatefrom'pinia-plugin-persistedstate';// 导入插件importAppfrom'./App.vue';constpinia=createPinia();pinia.use(piniaPluginPersistedstate);// 注册插件constapp=createApp(App);app.use(pinia);app.mount('#app');在 Store 中开启持久化:
// 选项式 API 写法exportconstuseUserStore=defineStore('user',{state:()=>({token:'',userInfo:null}),actions:{/* ... */},persist:true,// 开启持久化(默认存储到 localStorage)});// 自定义持久化配置exportconstuseCartStore=defineStore('cart',{state:()=>({items:[]}),persist:{key:'cart-store',// 自定义存储键名storage:sessionStorage,// 存储到 sessionStoragepaths:['items'],// 仅持久化 items 字段(默认全量)},});2. Store 间通信
一个 Store 可直接调用另一个 Store 的状态/方法:
// src/stores/cartStore.jsimport{defineStore}from'pinia';import{useUserStore}from'./userStore';// 导入其他 StoreexportconstuseCartStore=defineStore('cart',{state:()=>({items:[]}),actions:{asyncaddItem(item){constuserStore=useUserStore();// 创建其他 Store 实例if(!userStore.token){thrownewError('未登录,无法添加商品');}// 调用接口添加商品awaitfetch('/api/cart/add',{method:'POST',headers:{Authorization:userStore.token},body:JSON.stringify(item)});this.items.push(item);}}});3. 严格模式(禁止直接修改状态)
Pinia 默认允许直接修改state,开启严格模式后,仅允许通过actions修改状态(类似 Vuex 的严格模式):
// src/main.jsimport{createPinia}from'pinia';constpinia=createPinia();// 开启严格模式(仅生产环境建议关闭)if(import.meta.env.MODE!=='production'){pinia.use(({store})=>{store.$subscribe((mutation,state)=>{// 检测是否直接修改 state(非 actions 触发)if(mutation.type==='direct'){console.warn(`[Pinia 严格模式] 禁止直接修改${store.$id}的状态:${mutation.payload}`);}});});}4. 状态重置
调用$reset()方法重置 Store 到初始状态:
<script setup> import { useCounterStore } from '@/stores/counterStore'; const counterStore = useCounterStore(); const resetStore = () => { counterStore.$reset(); // 重置所有状态到初始值 }; </script>5. 批量修改状态
使用$patch批量修改状态(性能更优,DevTools 会记录为一次修改):
// 方式1:对象形式counterStore.$patch({count:10,title:'新标题'});// 方式2:函数形式(支持复杂逻辑)counterStore.$patch((state)=>{state.count+=5;state.userInfo.age+=1;});八、调试技巧
- Vue DevTools:Pinia 完美兼容 DevTools,可在「Pinia」面板查看所有 Store 的状态,支持「时间旅行」(回溯状态修改记录);
- $subscribe 监听状态变化:
constcounterStore=useCounterStore();// 监听状态变化counterStore.$subscribe((mutation,state)=>{console.log('状态变化:',mutation.type,mutation.payload,state);}); - $onAction 监听 action 调用:
counterStore.$onAction(({name,args,after,onError})=>{console.log(`开始执行 action:${name},参数:${args}`);after((result)=>{console.log(`action${name}执行成功,结果:${result}`);});onError((error)=>{console.error(`action${name}执行失败:${error}`);});});
九、常见问题
- 解构 Store 丢失响应式:用
storeToRefs解构状态,方法可直接解构; - Store 实例重复创建:无需担心,Pinia 会自动复用单例,多次调用
useCounterStore()始终返回同一个实例; - TypeScript 类型提示:选项式 API 自动推导类型,组合式 API 需手动标注(或用
defineStore自动推导); - SSR 适配:Pinia 原生支持 SSR,需在服务端为每个请求创建独立的 Pinia 实例,避免状态污染。
总结
Pinia 的核心用法可总结为:
- 安装并挂载 Pinia 实例;
- 用
defineStore定义 Store(选项式/组合式); - 组件中导入并创建 Store 实例,通过
storeToRefs解构响应式状态; - 用
actions处理同步/异步状态修改,getters处理计算属性; - 多 Store 实现模块化,插件扩展持久化、调试等能力。
相比 Vuex的繁琐,Pinia 更简洁、灵活,是 Vue3 项目状态管理的首选,建议在中大型项目中使用,小型项目可根据项目实际情况选择更加合适的。