# Pinia

# 什么是 Pinia?

Pinia(发音为/piːnjʌ ʌ/,如英语中的“peenya”)是最接近 piña(西班牙语中的菠萝)的词;

  • Pinia 开始于大概 2019 年,最初是作为一个实验为 Vue 重新设计状态管理,让它用起来像组合式 API(Composition API)。
  • 从那时到现在,最初的设计原则依然是相同的,并且目前同时兼容 Vue2、Vue3,也并不要求你使用 Composition API;
  • Pinia 本质上依然是一个状态管理的库,用于跨组件、页面进行状态共享(这点和 Vuex、Redux 一样);

# Pinia 和 Vuex 的区别

那么我们不是已经有 Vuex 了吗?为什么还要用 Pinia 呢?

  • Pinia 最初是为了探索 Vuex 的下一次迭代会是什么样子,结合了Vuex 5 核心团队讨论中的许多想法;
  • 最终,团队意识到Pinia 已经实现了 Vuex5 中大部分内容,所以最终决定用 Pinia 来替代 Vuex
  • 与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的仪式,提供了Composition-API 风格的 API
  • 最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持; 和 Vuex 相比,Pinia 有很多的优势:
  • 比如 mutations 不再存在:
    • 他们经常被认为是 非常冗长
    • 他们最初带来了 devtools 集成,但这不再是问题;
  • 更友好的 TypeScript 支持,Vuex 之前对 TS 的支持很不友好;
  • 不再有 modules 的嵌套结构
    • 可以灵活使用每一个 store,它们是通过扁平化的方式来相互使用的;
  • 也不再有命名空间的概念,不需要记住它们的复杂关系;

# 安装

npm: npm install pinia

# 配置 Pinia 入口

\src\stores\index.js

import { createPinia } from "pinia";

const pinia = createPinia();
export default pinia;

\src\main.js

import { createApp } from "vue";
import App from "./App.vue";
import pinia from "./stores";

const app = createApp(App);
app.use(pinia);

app.mount("#app");

# Store

  • 什么是 Store?

    • 一个 Store (如 Pinia)是一个实体,它会持有为绑定到你组件树状态和业务逻辑,也就是保存了全局的状态;
    • 它有点像始终存在,并且每个人都可以读取和写入的组件
    • 你可以在你的应用程序中定义任意数量的 Store 来管理你的状态
  • Store 有三个核心概念:

    • stategettersactions
    • 等同于组件的 data、computed、methods;
    • 一旦 store 被实例化,你就可以直接在 store 上访问 state、getters 和 actions中定义的任何属性;

# 定义 Store

我们需要知道 Store 是使用 defineStore() 定义的,

  • 并且它需要一个唯一名称,作为第一个参数传递;
  • 这个 name,也称为 id,是必要的,Pinia 使用它来将 store 连接到 devtools。
  • 返回的函数统一使用 useX 作为命名方案,这是约定的规范

\src\stores\Store 名.js

import { defineStore } from "pinia";

const useStore名 = defineStore("Store名", {
  state: () => ({
    变量:,
  }),
});

export default useStore名;

# 使用 Store

Store 在它被使用之前是不会创建的,可以通过调用 use 函数来使用 Store:

<template>
  <div>
    <h2>{{ Store名.变量名 }}</h2>
  </div>
</template>

<script setup>
  import useStore名 from "@/stores/Store名.js";

  const Store名 = useStore名();
</script>

注意 Store 获取到后不能被解构,那么会失去响应式:

const { 变量名 } = useStore名();

为了从 Store 中提取属性同时保持其响应式,您需要使用storeToRefs()

import { storeToRefs } from "pinia";
import useStore名 from "@/stores/Store名.js";

const { 变量名 } = storeToRefs(useStore名());

# 修改 State

  • 默认情况下,您可以通过 store 实例访问状态来直接写入状态;

    import useStore名 from "@/stores/Store名.js";
    
    const Store名 = useStore名();
    
    // 修改单个状态
    Store名.变量名 =;
    
    // 修改多个状态
    Store名.$patch({
      变量名:,
      变量名:,
    });
    
  • 重置 State:你可以通过调用 store 上的 $reset() 方法将状态 重置 到其初始值;

    import useStore名 from "@/stores/Store名.js";
    
    const Store名 = useStore名();
    Store名.$reset();
    
  • 替换 State:您可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态:

    import useStore名 from "@/stores/Store名.js";
    
    const Store名 = useStore名();
    
    Store名.$state = {
      变量名:,
      变量名:,
    };
    

# Getters (计算属性)

Getters 相当于 Store 的计算属性

  • 它们可以用 defineStore() 中的getters 属性定义;
  • getters 中可以定义接受一个 state 作为参数的函数

# 定义 Getters

\src\stores\Store名.js

  • 基本使用

    import { defineStore } from "pinia";
    
    const useStore名 = defineStore("Store名", {
      getters: {
        Getters名(state) {
          return state.变量名 * 2;
        },
      },
    });
    
    export default useStore名;
    
  • 传递参数

    import { defineStore } from "pinia";
    
    const useStore名 = defineStore("Store名", {
      getters: {
        Getters名(state) {
          return function (参数名) {
            return state.变量名 + 参数名;
          };
        },
      },
    });
    
    export default useStore名;
    
  • 引入另外的 getter

    import { defineStore } from "pinia";
    
    const useStore名 = defineStore("Store名", {
      getters: {
        Getters名1(state){
          return state.变量名 * 2;
        }
        Getters名2(state){
          return this.Getters名1 + 1;
        }
      }
    });
    
    export default useStore名;
    
  • 使用其他 Store 中的数据

    import { defineStore } from "pinia";
    import use其他Store名 from "./其他Store名.js";
    
    const useStore名 = defineStore("Store名", {
      getters: {
        Getters名(state) {
          const 其他Store名 = use其他Store名();
          return 其他Store名.变量名 + 1;
        },
      },
    });
    
    export default useStore名;
    

# 使用 Getters

<template>
  <div>
    <h2>{{ Store名.Getters名 }}</h2>
    <h2>{{ Store名.Getters函数名(参数值) }}</h2>
  </div>
</template>

<script setup>
  import useStore名 from "@/stores/Store名.js";

  const Store名 = useStore名();
</script>

# Actions (methods)

Actions 相当于组件中的 methods

  • 可以使用 defineStore() 中的 actions 属性定义,并且它们非常适合定义业务逻辑;
  • 和 getters 一样,在 action 中可以通过 this 访问整个 store 实例的所有操作;

# 基本使用

  • 定义 Actions

    \src\stores\Store名.js

    import { defineStore } from "pinia";
    
    const useStore名 = defineStore("Store名", {
      actions: {
        Actions名() {
          this.变量名 =;
        },
        // 传递参数
        Actions名(参数名) {
          this.变量名 = this.变量名 + 参数名;
        },
      },
    });
    
    export default useStore名;
    
  • 使用 Actions

    <template>
      <div>
        <h2>{{ Store名.变量名 }}</h2>
        <button @click="changeState">修改State</button>
      </div>
    </template>
    
    <script setup>
      import useStore名 from "@/stores/Store名.js";
    
      const Store名 = useStore名();
      function changeState() {
        Store名.Actions名();
        Store名.Actions名(参数值);
      }
    </script>
    

# 异步函数

Actions 中是支持异步操作的,并且我们可以编写异步函数,在函数中使用 await;

  • 定义 Actions

    \src\stores\Store名.js

    import { defineStore } from "pinia";
    
    const useStore名 = defineStore("Store名", {
      actions: {
        async Actions名() {
          const res = await fetch("http://url");
          const data = await res.json();
          this.变量名 = data;
          return data;
        },
      },
    });
    
    export default useStore名;
    
  • 使用 Actions

    <script setup>
      import useStore名 from "@/stores/Store名.js";
    
      const Store名 = useStore名();
      Store名.Actions名().then((res) => {
        console.log(res);
      });
    </script>
    
最后更新: 2022/12/19 17:39:03