Vue学习记录之二十七 Pinia的使用
Pinia 是一个用于 Vue.js 的状态管理库,旨在取代 Vuex,提供更现代的 API 和更好的开发体验。
一、安装
pnpm install pinia -s //运行时依赖
二、配置
在 main.ts中
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia' //导入依赖
const store = createPinia()
let app = createApp(App)
app.use(store) //注册组件
app.mount('#app')
三、创建一个 Store
src/store/index.ts
import { defineStore } from 'pinia'
import {Names} from './store-name'
export const useTestStore = defineStore(Names.TEST,{state:()=>{return {current: 20,name: "lvmanba"}},//computed 修饰一些值getters:{doubleCount: (state) => state.current * 2,},//methods 可以做同步,异步都可以做。提交stateactions:{setCurrent(num:number){this.current = num}}})// 热重载支持
if (import.meta.hot) {import.meta.hot.accept((newModule) => {useCounterStore.hotUpdate(newModule.useCounterStore);});
}
src/store/store-name.ts
export const enum Names{ //定义了一个名为Names的枚举TEST = 'TEST'
}
四、使用
<template><div>demo</div><div>pinia:{{Test.current}} - {{Test.name}}</div><div>{{ Test.doubleCount }}</div><div><button @click="change">修改pinia中的current</button></div>
</template>
<script setup lang='ts'>
import { ref,reactive } from 'vue'
import { useTestStore } from './store/index'
const Test = useTestStore()
// 方法1
/*
const change = () => {Test.current++
}
*/
// 方法2 通过patch
/*
const change = () => {Test.$patch({current: 888,name: "绿曼巴"})
}
*/
// 方法3, 还是使用patch, 他接收一个工厂函数,里面可以处理逻辑
/*
const change = () =>{Test.$patch((state)=>{ //state 就是我们pinia中的state.state.current++state.name = '方世玉'})
}
*/
// 方法4,不常用,有弊端,修改的话,需要修改整个对象,必须修改所有的值
/*
const change = () => {Test.$state = {current:2000,name:"少年没有乌托邦,心向远方自明朗"}
}
*/
// 方法5 使用defineStore定义的store中的action中的方法const change = () => {Test.setCurrent(567)
}</script>
<style scoped></style>
五、实例
代码的结构和上面一样: 功能是实现同步和异步调用。
App.vue
<template><div>demo<p> aciions-user: {{ Test.user}}</p><hr /><p> aciions-name: {{ Test.name}}</p><hr /><p> getters: {{ Test.newName}}</p><hr><button @click="change">修改pinia中的current</button></div>
</template>
<script setup lang='ts'>
import { ref,reactive } from 'vue'
import { useTestStore } from './store/index'
const Test = useTestStore()const change = () => {Test.setUser()
}
</script>
<style scoped></style>
src/store/index.ts
import { defineStore } from 'pinia'
import {Names} from './store-name'type User = {name: string,age:number
}
/*
// 同步
let result:User = {name:"飞机",age: 18
}
*/const Login = ():Promise<User> => {return new Promise((resolve)=>{setTimeout(()=>{resolve({name: 'lmb',age: 42})},2000)})
}export const useTestStore = defineStore(Names.TEST,{state:()=>{return {user:<User>{},name: "小飞机"}},//computed 修饰一些值getters:{newName():string{ //需要设置返回值类型,要不报错。return `$-${this.name}-${this.getUserAge}`},getUserAge():number{return this.user.age}},//methods 可以做同步,异步都可以做。提交stateactions:{async setUser(){const result = await Login();this.user = result;this.setName('大飞机')},setName(name:string){this.name = name}}})
六、Pinia的API
1、$reset()
调用reset可以让值恢复到state定义的原始值
如在App.vue
<template><div><button @click="reset">reset</button></div>
</template>
<script setup lang='ts'>
import { ref,reactive } from 'vue'
import { useTestStore } from './store/index'
const Test = useTestStore()const reset = () => {Test.$reset()
}
</script>
2、$subscribe
$subscribe 方法用于监听 store 中状态的变化,只要发生变化,就自动调用。它接受一个回调函数,该函数会在每次 store 状态发生变化时被调用。回调函数有两个参数:
- mutation:一个对象,包含关于状态变化的信息。典型的字段包括 storeId(store 的 ID)、type(变化的类型,例如 patch 或 direct)、events(变化的详细信息)。
- state:变化后的新状态。
const unsubscribe = Test.$subscribe((mutation, state) => {console.log(mutation); // mutation 是一个描述状态变化的对象console.log(state); // state 是变化后的新状态
});// 如果你想取消订阅,可以调用 unsubscribe 函数
unsubscribe();
3、$onAction
$onAction 是一个方法,用于监听 store 中 action 的调用。通过这个方法,你可以在 action 被调用之前和之后执行一些代码,比如记录日志、性能监控或错误处理等。
const off = store.$onAction(({ name, store, args, after, onError }) => {console.log(`Action ${name} was called with args:`, args);// 在 action 执行后执行一些代码。一个函数,接受一个回调函数,该回调函数会在 action 执行完毕后被调用,并传入 action 的返回值。after((result) => {console.log(`Action ${name} finished with result:`, result);});// 在 action 执行时发生错误时执行一些代码onError((error) => {console.error(`Action ${name} failed with error:`, error);});
});// 如果你想取消监听,可以调用 off 函数
off();
七、常见的问题
1、Pinia 支持热重载,但是需要单独设置,你可以在开发过程中修改 store 而无需刷新整个页面:
if (import.meta.hot) {import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot));
}
2、解构出来的变量不具备响应性,需要用到storeToRefs
import { storeToRefs } from 'pinia'
// const {current,name} = Test
// 这样current,name就具备了响应式
const {current,name} = storeToRefs(Test)
八、持久化
pinia和vuex 都有一个通病页面刷新状态会丢失。我们可以写一个pinia插件缓存他的值。
可以使用 pinia-plugin-persist 插件。这个插件允许你将状态存储到 LocalStorage 或 SessionStorage 中,从而在页面刷新后保留状态。
npm install pinia pinia-plugin-persist
配置
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import piniaPersist from 'pinia-plugin-persist'; //导入依赖
import App from './App.vue';const app = createApp(App);
const pinia = createPinia();pinia.use(piniaPersist); //注册插件app.use(pinia);
app.mount('#app');
使用:
import { defineStore } from 'pinia';export const useMainStore = defineStore({id: 'main',state: () => ({counter: 0,user: {name: 'John Doe',age: 30}}),actions: {increment() {this.counter++;},setUser(name, age) {this.user.name = name;this.user.age = age;}},persist: {enabled: true,strategies: [{key: 'main_store',storage: localStorage, // 你也可以使用 sessionStorage},],},
});
读取存储
<template><div><p>Counter: {{ counter }}</p><button @click="increment">Increment</button></div>
</template><script setup lang="ts">
import { useMainStore } from './stores/main';// 获取 store 实例
const mainStore = useMainStore();// 解构需要的状态和方法
const { counter, increment } = mainStore;
</script>