Vue3 官推的状态管理 Pinia
- 一、Pinia是什么?
- 二、Pinia的特点
- 三、Pinia的使用
- 1.npm install pinia -s
- 2.创建pinia实例
- 3.注册到App实例上
- 4.模块化管理
- 5.组件中使用
- 6.路由中使用
- 1.创建全局路由守卫
- 2.全局守卫中使用全局状态
- 四、修改数据 $patch
- 五、重置数据 $reset
- 六、监听数据 $subscribe
- 七、监听Actions $onAction()
- Tip:可以在pinia实例上使用watch()函数侦听整个state。
- Tip:不使用setup时。
一、Pinia是什么?
在Vue2中一般采用Vuex进行状态管理,在Vue3中推荐使用Pinia,这Vue3中推荐使用Pinia,这是最新一代轻量级状态管理插件。Vuex将不再接受新的功能。推荐使用Pinia。
二、Pinia的特点
- 支持Vue2和Vue3,两者都可以使用Pinia;
- 语法简洁,支持Vue3中的setup的写法,不必像Vue2中那样定义state、mutations、actions、getters等,可以按照setup Composition API的方式返回状态和改变状态的方法实现代码的扁平化;
- 支持Vuex中state、actions、getters形式的写法,丢弃了mutations开发时候不用根据同步异步来决定mutation或者actions,Pinia中只有actions;
- 对TyppeScript支持非常友好;
三、Pinia的使用
1.npm install pinia -s
2.创建pinia实例
// src/store/index.ts
import { createPinia } from 'pinia';const pinia = createPinia();
export default pinia ;
3.注册到App实例上
// src/main.ts
import App from './App.vue';
import { createApp } from 'vue';
import pinia from '@/store';const app = createApp(App);
app.use(pinia);
4.模块化管理
defineStore()方法的第一个参数:为仓库起一个名字呢,不可重复,唯一的;
defineStore()方法的第二个参数:有setup和option两种写法
// src/store/modules/demo.ts(setup写法)
import { defineStore } from 'pinia';
import { store } from '/@/store';const useDemoStore = defineStore('demo',()=>{const counter = ref(0);const increment = ()=>{counter.value++;}return {counter,increment}
});
export default useDemoStore;
// src/store/modules/demo.ts(option写法)
import { defineStore } from 'pinia';
import { store } from '/@/store';export const useDemoStore = defineStore('demo',{state:()=>{return {name:'张三',count:0}},getters:{getName:(state)=>{// return this.name+'getName';return state.name+'getName';return (id)=>state.list.filter(v=>v.id===id);//getters传参}},actions:{updateName:(val:string)=>{this.name=val;}},persist:true
});
state:用来存储全局的状态,这边定义的,可以全局访问;
getters:和Vuex一样,来监视或者计算状态变化;具有缓存功能,组件中调用多次,实际在store中只执行了一次;
actions:适合处理修改逻辑复杂的数据,可以在actions中定义好函数,然后在组件中调用,是state数据相关的业务逻辑,需求不同,逻辑不同。
注:getters和actions里可以直接使用this。
5.组件中使用
// src/views/Demo.vue
<template><h1>counter:{{counter}}</h1><el-button @click="add"> 自增 </el-button>
</template>
<script setup lang="ts">import { storeToRefs} from 'pinia';import useDemoStore from '/@/store/modules/demo.ts';const demoStore = useDemoStore();const { counter } = storeToRefs(demoStore);const add = ()=>{demoStore.increment();}console.log(demoStore.getName());
</script>
注:无论是pinia还是vuex,通过解构的方式获取状态,会导致状态失去响应性。如:
const { counter } = demoStore;
此时counter丢失了响应性,当其值发生改变时,其他组件不会监听到。所以pinia提供了storeToRefs函数,使其解构出来的状态仍然具有相应性。
const { counter } = storeToRefs(demoStore);
6.路由中使用
演示在全局路由守卫中获取状态值。创建一个路由守卫,在路由守卫中使用nprogress显示页面加载进度。
1.创建全局路由守卫
- 安装nprogress
npm install nprogress
npm install @types/nprogress -D
- 创建全局路由守卫
// src\router\guard\index.ts
import nProgress from 'nprogress';
import 'nprogress/nprogress.css';
nProgress.configure({showspinner:false,
})
// 全局前置守卫
router.beforeEach((to,from)=>{nProgress.start();return true;
})
// 全局后置守卫
router.afterEach((to,from)=>{nProgress.done(true);
})
3.在main.ts中引入全局守卫路由
import '@/router/guard/index'
2.全局守卫中使用全局状态
实际开发中,路由切换时,可能需要从全局状态中获取token等信息,判断是否能进入下一页面。
// src\router\guard\index.ts
...
import useDemoStore from '@/store/modules/demo'
import {storeToRefs} from 'pinia'
...
const demoStore = useDemoStore();
const {counter} = storeToRefs(demoStore);
// 全局前置守卫
router.beforeEach((to,from)=>{nProgress.start();console.log(counter);return true;
})
在钩子函数外,pinia还没有挂载,所以会报错,应该修改后如下:
// src\router\guard\index.ts
import router from '@/router';
import nProgress from 'nprogress';
import 'nprogress/nprogress.css';
import useDemoStore from '@/store/modules/demo'
import {storeToRefs} from 'pinia'
nProgress.configure({showspinner:false,
})
// 全局前置守卫
router.beforeEach((to,from)=>{nProgress.start();const demoStore = useDemoStore();const {counter} = storeToRefs(demoStore);console.log(counter);return true;
})
// 全局后置守卫
router.afterEach((to,from)=>{nProgress.done(true);
})# 四、Pinia状态持久化## 1.为什么需要状态持久化?
因为状态存储到浏览器内存中,刷新浏览器后,重新加载页面会重新初始化vue.pinia,导致浏览器存储中的数据丢失。
实际项目中,浏览器刷新时,这有些数据希望保存下来。
解决方案:状态改变时将其同步到浏览器的存储中,如cookie,localStorage,sessionStorage,每次初始化状态时从存储中获取初始值即可。
## 2.插件pinia-plugin-persistedstate1. npm install pinia-plugin-persistedstate 2. src/store/index.ts
```typescript
import { createPinia } from 'pinia';
import piniaPluginPersistedState from 'pinia-plugin-persistedstate';const pinia = createPinia();
pinia.use(piniaPluginPersistedState);
export default pinia;
- 模块管理中的使用:
// src/store/modules/demo.ts
import { defineStore } from 'pinia';
import { store } from '/@/store';const useDemoStore = defineStore('demo',()=>{const counter = ref(0);const increment = ()=>{counter.value++;}return {counter,increment}
},{ persist:true });
export default useDemoStore;
persist : 值为true,状态存储在localStorage中;该localStorage中的key为模块名“demo”。
如何修改key?如何将状态存储到SessionStorage?
{persist:{key:"byyourself",storage:"sessionStorage",}
}
四、修改数据 $patch
const updataName = () =>{// 1.修改单个数据demoStore.name='李四';// 2.修改多个数据-$patchdemoStore.$patch({name:'李四',count:18)// 3.修改复杂数据-$patch+函数demoStore.$patch((state)=>{state.name='李四';})// 4.使用action处理数据demoStore.updateName('李四')
}
五、重置数据 $reset
const resetName = ()=>{demoStore.$reset();
}
pinia setup 方式构建$reset方法失效解决方案:
import { creatPinia } from 'pinia';
const pinia = creatPinia();
pinia.use((store)=>{const initialStata=JSON.parse(JSON.stringify(store.$state));store.$reset=()=>{store.$patch(initialStata)}
})
六、监听数据 $subscribe
const subscribeName = demoStore.$subscribe((mutations,state)=>{// 监听store中的某个值变化,处理一些逻辑// $subscribe第一个参数mutations,包含了3个属性:// 1.events:改变的具体数据,newValue,oldValue;// 2.storeId:当前store的Id,这里是'demo';// 3.type:记录改变数据途径(direct:通过actions改变;patch object:// 提供$patch传递的方式改变的;patch function:通过$patch传递函数)// $subscribe第二参数,是options对象,是各种参数配置// imemediate,deep和vue2中的watch里的参数一样;// detached:默认是false,正常情况下,当订阅所在的组件被卸载时,订阅失败;// 当值为true时,即便订阅的组件被卸载,订阅依然生效;},{imemediate:false,deep:false,detached:false})// 停止订阅监听subscribeName();
七、监听Actions $onAction()
const subAction=demoStore.$onAction((name,store,args,after,onError)=>{// name: actions的名字;// store: store实例;// args: 调用这个actions用的参数;// after((result)=>{// // 在action执行完毕后执行的逻辑// })// onError((result)=>{// // 在action执行异常后执行的逻辑// })},true)// true组件卸载后,监听依旧保留// 停止监听ActionssubAction();
Tip:可以在pinia实例上使用watch()函数侦听整个state。
watch(pinia.state,(state)=>{// 每当状态发生变化时,将整个state持久化到本地存储localStorage.setItem(,JSON.stringify(state))},}{deep:true})
Tip:不使用setup时。
import { mapState, mapActions } from 'pinia';import { mainStore } from '@/store/index.ts'export default {computed:{...mapState(mainStore,['name','count','getName']),// 定义myOwnName:"name"},// 可以在组件中通过this.name访问methods:{...mapActions(mainStore,['updateName']),d// 定义myOwnName:"updateName"},// 可以在组件中通过this.updateName()调用}}