Skip to content

Pinia

https://pinia.vuejs.org/zh/

Pinia 最初是在 2019 年 11 月左右重新设计使用 Composition API。从那时起,最初的原则仍然相同,但 Pinia 对 Vue 2 和 Vue 3 都有效,并且不需要您使用组合 API。除了安装和 SSR 之外,两者的 API 都是相同的,并且这些文档针对 Vue 3,并在必要时提供有关 Vue 2 的注释,以便 Vue 2 和 Vue 3 用户可以阅读!

为什么要使用 Pinia?

Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。如果您熟悉 Composition API,您可能会认为您已经可以通过一个简单的 export const state = reactive({}). 这对于单页应用程序来说是正确的,但如果它是服务器端呈现的,会使您的应用程序暴露于安全漏洞。 但即使在小型单页应用程序中,您也可以从使用 Pinia 中获得很多好处:

  • dev-tools 支持
    • 跟踪动作、突变的时间线
    • Store 出现在使用它们的组件中
    • time travel 和 更容易的调试
  • 热模块更换
    • 在不重新加载页面的情况下修改您的 Store
    • 在开发时保持任何现有状态
  • 插件:使用插件扩展 Pinia 功能
  • 为 JS 用户提供适当的 TypeScript 支持或 autocompletion
  • 服务器端渲染支持

pinia 核心概念

  • state: 状态
  • getters: 计算属性
  • actions: 修改状态(包括同步和异步,pinia 中没有 mutations)

基本使用

1、安装

shell
npm i pinia

2、在 main.js 中挂载 pinia

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

const app = createApp(App);

app.use(createPinia());

app.mount("#app");

组合式 Store

使用 setup 模式定义,符合 Vue3 setup 的编程模式,让结构更加扁平化,推荐推荐使用这种方式。

新建文件store/counter.js

js
import { ref, computed } from "vue";
import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", () => {
  // state
  const count = ref(100);
  // getters
  const doubleCount = computed(() => count.value * 2);
  // mutations
  const increment = () => count.value++;
  // actions
  const incrementAsync = () => {
    setTimeout(() => {
      count.value++;
    }, 1000);
  };
  return { count, doubleCount, increment, incrementAsync };
});

App.vue 中使用

vue
<script setup>
import { useCounterStore } from "./stores/counter.js";

const counter = useCounterStore();
</script>

<template>
  <div>
    <h1>根组件 --- {{ counter.count }}</h1>
    <h3>{{ counter.doubleCount }}</h3>
    <button @click="counter.increment">加 1</button>
    <button @click="counter.incrementAsync">异步加 1</button>
  </div>
</template>

选项式 Store(演示)

使用 options API 模式定义,这种方式和 vue2 的组件模型形式类似,也是对 vue2 技术栈开发者较为友好的编程模式。但是这种技术在 vue3 中几乎不用了,稍微看看即可。

state 的使用

新建文件 store/counter_options.js

js
import { defineStore } from "pinia";
// 创建 store,命名规则:useXxxStore
// 参数 1:store_id 的唯一表示
// 参数 2:对象,可以提供 state actions getters
export const useCounterStore = defineStore("counter", {
  state: () => {
    return {
      count: 0,
    };
  },
});

getters 的使用

1、在 getters 中提供计算属性,如果需要修改请写在 actions

js
import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", {
  state: () => {
    return {
      count: 0,
    };
  },
  getters: {
    doubleCount() {
      return this.count * 2;
    },
  },
});

2、在 App.vue 组件中使用

vue
<template>
  <div>
    <h1>根组件 --- {{ counter.count }}</h1>
    <h3>{{ counter.doubleCount }}</h3>
  </div>
</template>

actions 的使用

在 pinia 中没有 mutations,只有 actions,不管是同步还是异步的代码,都可以在 actions 中完成。

1、在 actions 中提供方法并且修改数据

js
import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", {
  state: () => {
    return {
      count: 0,
    };
  },
  getters: {
    doubleCount() {
      return this.count * 2;
    },
  },
  actions: {
    increment() {
      this.count++;
    },
    incrementAsync() {
      setTimeout(() => {
        this.count++;
      }, 1000);
    },
  },
});

2、在 App.vue 组件中使用

vue
<script setup>
// import {useCounterStore} from "./stores/counter.js";
import { useCounterStore } from "./stores/counter_options.js";

const counter = useCounterStore();
console.log(counter.count);
console.log(counter.count);
</script>

<template>
  <div>
    <h1>根组件 --- {{ counter.count }}</h1>
    <h3>{{ counter.doubleCount }}</h3>
    <button @click="counter.increment">加 1</button>
    <button @click="counter.incrementAsync">异步加 1</button>
  </div>
</template>

对比总结

上面 2 种定义方式效果都是一样的,我们用 defineStore 方法定义一个 store,里面分别定义 1 个 countstate ,1 个 increment 的 action 和 1 个 doubleCountgetters。其中 state 是要共享的全局状态,而 action 则是让业务方调用来改变 state 的入口,getters 是获取 state 的计算结果。

使用方式是通过 const useXxxStore = defineStore('id',函数) 创建 store 得到使用的对象

选项式组合式
staterefreactive 创建的响应式数据
gettercomputed 创建的计算属性
actions普通函数,异步函数都可以

storeToRefs 辅助函数

如果直接从 pinia 中解构数据,会丢失响应式,使用 storeToRefs 可以保证解构出来的数据也是响应式的

vue
<script setup>
import { useCounterCompositionStore } from "./store/counter.js";
import { storeToRefs } from "pinia";

const counter_composition = useCounterCompositionStore();

// 如果直接从 pinia 中解构数据,会丢失响应式
// const {count, doubleCount} = counter_composition

// 使用 storeToRefs 可以保证解构出来的数据也是响应式的
const { count, doubleCount } = storeToRefs(counter_composition);
</script>