使用Pinia
大约 6 分钟
Pinia全局状态管理
一、Pinia基本使用
1.安装 Pinia
yarn add pinia
npm i pinia
2.注册Pinia
import { createApp } from "vue";
import App from "./App.vue";
// 导航pinia的createPinia方法
import { createPinia } from "pinia";
// 创建 pinia实例
const pinia = createPinia();
const app = createApp(App);
// 注册pinia插件
app.use(pinia);
app.mount("#app");
3.定义Store
import { defineStore } from "pinia";
// 定义Store ,Store的命名推荐使用 usexxxStore 这种写法
export const useCountStore = defineStore("count", {
// Option对象,有三个重要的属性 state、actions 与 getters,
// state 相当于组件中的data
// 为了完整类型推理,state函数更推荐箭头函数写法
state: () => {
return {
count: 10,
};
},
// getters 相当于组件中的computed,会对数据缓存
getters: {
doubleCount: (state) => state.count * 2,
},
// actions 相当于组件中的methods
actions: {
add() {
this.count++; // this指向创建的store
},
},
});
import { ref, computed, reactive } from "vue";
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", () => {
// 定义的ref变量,相当于Option对象中的state 即相当于组件中的data
const username = ref("艾编程");
const age = ref(17);
const hobbies = reactive(["画画", "写字"]);
// 计算属性 相当于Option对象的getters,即相当于组件中的computed
// 返回的计算属性 identity 是一个ref对象,所以是响应式的
const identity = computed(() => {
return age.value >= 18 ? "成年" : "未成年";
});
// 函数,相当于Option对象的actions,即相当于组件中的methods方法
function update() {
age.value++;
}
// 对外暴露属性和方法
return {
username,
age,
hobbies,
identity,
update,
};
});
4.使用Store
<script setup>
import { useCountStore } from "./store/count";
// countStore为创建的store实例,可以在组件的任意位置访问countStore
const countStore = useCountStore();
</script>
<template>
<div>count的值:{{ countStore.count }}</div>
<div>count放大2倍值:{{ countStore.doubleCount }}</div>
<button @click="countStore.add">count++</button>
</template>
<script setup>
import { useUserStore } from "./store/user";
// countStore为创建的store实例,可以在组件的任意位置访问countStore
const userStore = useUserStore();
</script>
<template>
<div>用户姓名:{{ userStore.username }}</div>
<div>用户年龄:{{ userStore.age }}</div>
<!--
identity计算属性虽然是一个ref对象,但是userStore是一个用reactive包装的响应式对象
针对响应式对象的ref属性,在模板中调用时会自动解包,所以模板中调用不需要`.value`
-->
<div>用户身份:{{ userStore.identity }}</div>
<button @click="userStore.update">更新年龄</button>
</template>
5.解构Store
- 为了从 store 中提取属性时保持其响应性,你需要使用
storeToRefs()
。- 该方法类似于 Vue 中的
toRefs()
,最终解构出来的每个属性都是一个单的ref
对象。不过要特别注意,store 实例中的方法是可以直接从 store 实例解构的
<script setup>
import { storeToRefs } from "pinia";
import { useUserStore } from "./store/user";
const UserStore = useUserStore();
// 直接解构store,会失去响应性需要输液storeToRefs()
const { username, age, identity } = storeToRefs(UserStore);
// 方法,可以直接解构
const { update } = UserStore;
</script>
<template>
<div>用户姓名:{{ username }}</div>
<div>用户年龄:{{ age }}</div>
<div>用户身份:{{ identity }}</div>
<button @click="update">更新年龄</button>
</template>
二、深入 State
1.直接修改 state 的值
我们可以通过 store 实例访问 state,直接修改其值
<script setup>
import { storeToRefs } from "pinia";
import { useUserStore } from "./store/user";
const userStore = useUserStore();
function update() {
userStore.username = "清心";
userStore.age = 33;
}
</script>
<template>
<div>用户姓名:{{ userStore.username }}</div>
<div>用户年龄:{{ userStore.age }}</div>
<div>用户身份:{{ userStore.identity }}</div>
<button @click="update">更新数据</button>
</template>
2.批量修改 State 的值
调用 store 实例的
$patch
方法,它允许你用一个state
的补丁对象在同一时间更改多个属性。
<script setup>
import { storeToRefs } from "pinia";
import { useUserStore } from "./store/user";
const userStore = useUserStore();
function update() {
userStore.$patch({
username: "清心",
age: 33,
});
}
</script>
<template>
<div>用户姓名:{{ userStore.username }}</div>
<div>用户年龄:{{ userStore.age }}</div>
<div>用户身份:{{ userStore.identity }}</div>
<button @click="update">更新用户信息</button>
</template>
3.批量修改函数形式
- 你可以向 store 实例的
$patch
方法传入一个函数作为参数来实现一次性修改多个 state 属性值- 该回调函数接受 state 作为第一个参数
<script setup>
import { storeToRefs } from "pinia";
import { useUserStore } from "./store/user";
const userStore = useUserStore();
// 修改state中多个属性的值
function update() {
userStore.$patch((state) => {
state.username = "清心";
state.age = 33;
state.hobbies.push("唱歌");
});
}
</script>
<template>
<div>用户姓名:{{ userStore.username }}</div>
<div>用户年龄:{{ userStore.age }}</div>
<div>用户身份:{{ userStore.identity }}</div>
<div>用户爱好:{{ userStore.hobbies }}</div>
<button @click="update">更新用户信息</button>
</template>
4.通过 actions 修改
我们可以在 Store 的 actions 选项中定义方法来修改 state 中的值
以下代码中的 update 方法用来修改 state 属性中的值
import { ref, computed, reactive } from "vue";
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", () => {
// 定义的ref变量,相当于Option对象中的state 即相当于组件中的data
const username = ref("艾编程");
const age = ref(17);
const hobbies = reactive(["画画", "写字"]);
// 计算属性 相当于Option对象的getters,即相当于组件中的computed
const identity = computed(() => {
return age.value >= 18 ? "成年" : "未成年";
});
// 函数,相当于Option对象的actions,即相当于组件中的methods
function update() {
username.value = "清心";
age.value = 33;
hobbies.push("喝歌");
}
// 对外暴露属性和方法
return {
username,
age,
identity,
update,
hobbies,
};
});
5.注意事项
我们没有办法替换掉
store
的state
,因为那样会破坏其响应性。即使你使用
store.$state
来重新给 state 赋值,也无计于事。因为store.$state
代码内部本质是调用了sotre.$patch
来实现打补丁修改state
某些属性值。
// 以下代码实际上并没有替换掉整个state
userStore.$state = { username: "清心" };
// 上面代码相当于内部调用了$patch()方法,如下
userStore.$patch({ username: "清心" });
6.订阅 state
你可以通过 store 的
$subscribe()
方法侦听 state 及其变化。当 state 的值发生改变时就会触发$subscribe()
方法及回调函数。比起普通的watch()
,使用$subscribe()
的好处是 subscriptions 在 patch 后只触发一次使用
store.$patch()
方法一次修改多个 state 的值,只会触发一次$subscribe()
方法的执行
const stop = store.$subscribe(
(mutation, state) => {
/*
* mutation主要包含三个属性值:
* events:当前state改变的具体数据都在该对象上
* storeId:是当前store的id
* type:用于记录这次数据变化是通过什么途径,主要有三个分别是
* “direct” :直接修改 , 如:store.count++
”patch object“ :通过 $patch 传递对象的方式改变的
“patch function” :通过 $patch 传递函数的方式改变的
*
* */
// 我们就可以在此处监听store中值的变化,当变化为某个值的时候,去做一些业务操作之类的
},
{
/*
* 第二个参数options对象,是各种配置参数
* detached:布尔值,默认是 false,正常情况下,当订阅所在的组件被卸载时,订阅将被停止删除, 如果设置detached值为 true 时,即使所在组件被卸载,订阅依然在生效
* immediate,deep,flush等等参数 和vue3 watch的参数是一样的
*
* */
}
);
// 停止订阅
stop();
import { ref } from "vue";
import { defineStore } from "pinia";
export const useCountStore = defineStore("count", () => {
const count = ref(10);
const num = ref(1);
return {
count,
num,
};
});
<script setup>
import { watch } from "vue";
import { storeToRefs } from "pinia";
import { useCountStore } from "./store/count";
const countStore = useCountStore();
// 解构state
const { count, num } = storeToRefs(countStore);
// 修改值
function add() {
countStore.$patch(() => {
count.value++;
num.value++;
});
}
// 订阅
const stop = countStore.$subscribe((mutation, state) => {
console.log("改变后count的值", state.count);
});
</script>
<template>
<div>count的值:{{ count }}</div>
<div>num的值{{ num }}</div>
<button @click="add">count++</button>
<button @click="stop()">停止订阅</button>
</template>