跳至主要內容

使用Pinia

xiaoye大约 6 分钟VuePinia

Pinia全局状态管理

一、Pinia基本使用

1.安装 Pinia

yarn add 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
    },
  },
});

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>

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.注意事项

我们没有办法替换掉storestate,因为那样会破坏其响应性。

即使你使用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();

三、深入 Getter