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 可以做同步,异步都可以做。提交state
actions:{
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 可以做同步,异步都可以做。提交state
actions:{
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>