vue3使用Pinia全局状态管理

3,697 阅读3分钟

Pinia 是 Vue.js 的轻量级状态管理库

官方网站:pinia.vuejs.org/

中文文档(非官方,翻译了大部分内容):pinia.web3doc.top

为什么学习pinia?

  • pinia和vuex4一样,也是vue官方的状态管理工具(作者是 Vue 核心团队成员)
  • pinia相比vuex4,对于vue3的兼容性更好
  • pinia相比vuex4,具备完善的类型推荐
  • pinia同样支持vue开发者工具,最新的开发者工具对vuex4支持不好
  • Pinia 的 API 设计非常接近 Vuex 5提案

pinia核心概念

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

基本使用与state

目标:掌握pinia的使用步骤

(1)安装

yarn add pinia
# or
npm i pinia

(2)在main.js中挂载pinia

import { createApp } from 'vue'
import App from './App.vue'import { createPinia } from 'pinia'
const pinia = createPinia()
​
createApp(App).use(pinia).mount('#app')

(3)新建文件store/index.js

import { defineStore } from "pinia";
// 创建store,命名规则: useXxxxStore
// 参数1:store的唯一 id
// 参数2:对象,可以提供state actions getters
const useCounterStore = defineStore("counter", {
  // 数据 data
  state: () => {
    return {
      count: 100,
    };
  },
  //   计算 cpmputed
  getters: {},
  //   方法 methods
  actions: {},
});
​
export default useCounterStore;
​

(4) 在组件中使用

<script setup>
import useCounterStore from './store';
​
const counterStore = useCounterStore()
</script><template>
  <h1>Hello vue3</h1>
  <div>{{ counterStore }}</div>
  <!-- 不需要 .state.count,可以直接读取到数据,和vue2语法一样 -->
  <div>{{ counterStore.count }}</div>
</template><style scoped>
</style>

getters的使用

pinia中的getters和vuex中的基本是一样的,也带有缓存的功能

(1)在getters中提供计算属性

import { defineStore } from "pinia";
// 创建store,命名规则: useXxxxStore
// 参数1:store的唯一 id
// 参数2:对象,可以提供state actions getters
const useCounterStore = defineStore("counter", {
  // 数据 data
  state: () => {
    return {
      count: 100,
      //   有同名属性,会以 getters 为准
      double: 500,
    };
  },
  //   计算 cpmputed
  getters: {
    double() {
      // 不用再写state,通过 this 使用即可
      return this.count * 2;
    },
  },
  //   方法 methods
  //   没有 mutations,同步异步都可以在 actions 完成
  actions: {
    add() {
      this.count++;
    },
    addAsync() {
      setTimeout(() => {
        this.count++;
      }, 2000);
    },
  },
});
​
export default useCounterStore;
​

(2)在组件中使用

​
  <!-- 不需要 .getters.double,可以直接读取到数据 -->
  <div>{{ counterStore.double }}</div>

actions的使用

目标:掌握pinia中actions的使用

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

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

import { defineStore } from 'pinia'
// 1. 创建store
// 参数1:store的唯一表示
// 参数2:对象,可以提供state actions getters
const useCounterStore = defineStore('counter', {
  actions: {
    add() {
      this.count++;
    },
    addNum(val) {
      this.count+=val;
    },
    addAsync() {
      setTimeout(() => {
        this.count++;
      }, 2000);
    },
  },
})
​
export default useCounterStore

(2)在组件中使用

<template>
  <button @click="counterStore.add">同步相加</button>
  <button @click="counterStore.addNum(10)">传参相加</button>
  <button @click="counterStore.addAsync">异步相加</button>
</template>

storeToRefs的使用

目标:掌握storeToRefs的使用

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

<script setup>
import { storeToRefs } from 'pinia';
import { toRefs } from 'vue';
import useCounterStore from './store';
​
const counterStore = useCounterStore()
​
// 非响应式
// const { count } = counterStore
// 响应式,用我就对了
const { count } = toRefs(counterStore)
// 响应式,pinia提供,减少不必要的响应式属性和方法
// const { count } = storeToRefs(counterStore)
</script>

pinia模块化

pinia模块化介绍

在复杂项目中,不可能吧多个模块的数据都定义到一个store中,一般来说会一个模块对应一个store,最后通过一个根store进行整合

(1)新建store/user.js文件

import { defineStore } from 'pinia'
// vue2 模块化
// this.$store.user.state
// this.$store.counter.stateconst useUserStore = defineStore('user', {
  state: () => {
    return {
      name: 'zs',
      age: 100,
    }
  },
})
​
export default useUserStore
​

(2)新建store/index.js

import useCounterStore from "./modules/counter";
import useUserStore from "./modules/user";
​
// 写在外面,加载完就不会再调用了,是一次性的
// const counter = useCounterStore();
// const user = useUserStore();// 通过属性调用,每次都保证拿到最新的 store
const useStore = () => {
  return {
    counter: useCounterStore(),
    user: useUserStore(),
  };
};
​
export default useStore;
​

(3)在组件中使用

<script setup>
import { toRefs } from 'vue';
import useStore from './store'
const { user, counter } = useStore()
​
const { double } = toRefs(counter)
</script><template>
  <!-- 所有的方法都测一下 -->
  <h1>Hello vue3</h1>
  <div>{{ user }}</div>
  <div>{{ counter }}</div>
  <div>{{ user.name }}</div>
  <div>{{ counter.count }}</div>
  <button @click="counter.add">同步+1</button>
  <button @click="counter.addNum(10)">+10</button>
  <button @click="counter.addAsync">异步加1</button>
​
​
  <div>{{ double }}</div>
</template><style scoped>
</style>

\