Skip to content

使用Pinia集中管理数据

在Vue3中多个组件共享数据使用Pinia🍍,不再支持VueX。相比VueX,Pinia的优势:

  • 完整的ts支持
  • 足够轻量,压缩后的体积只有1kb左右;
  • 去除mutations,只有state, getters,actions;
  • actions支持同步和异步;
  • 代码扁平化没有模块嵌套,只有store的概念,store之间可以自由使用,每一个store都是独立的
  • 无需手动添加store, store一旦创建便会自动添加
  • 支持Vue3和Vue2
  • 支持Composition API
  • 支持插件扩展 Pinia 功能

1.安装Pinia

sh
npm i pinia

2.创建store文件夹,创建index.ts文件

ts
// 1. 引入pinia
import { createPinia } from "pinia"
import { createPersistedState } from 'pinia-plugin-persistedstate'
// 2. 创建pinia
const pinia = createPinia()

pinia.use(createPersistedState({
  auto: true,
  storage: sessionStorage
}))

export default pinia

3. 在main.ts中配置

ts
import { createApp } from 'vue'

import App from './App.vue'
import router from "./router"
import pinia from "./store"

const app = createApp(App)
// 3. 安装pinia
app.use(pinia)
app.use(router)
app.mount('#app')

4. 在需要使用的组件编写代码

vue
<template>
  <div style="width: 256px">
    <a-menu 
      v-model:openKeys="menuState.openKeys" 
      v-model:selectedKeys="menuState.selectedKeys" 
      mode="inline" 
      theme="dark"
      :inline-collapsed="menuState.collapsed" 
      :items="items">
    </a-menu>
  </div>
</template>
<script lang="ts" setup>
import { watch } from 'vue'
import { useMenuStateStore, useMenuDataStore } from "../store/menu"

const menuState = useMenuStateStore()
// 获取数据items有两种方式:
// 1. 从$state中获取 const items = useMenuDataStore().$state.items
// 2. 直接获取  const items = useMenuDataStore().items
// 如果运用对象解构:items将脱离state管理,下面这行代码是错误的,
// 如需读取方便请参考下一节storeToRefs
const { items } = useMenuDataStore() 

watch(
  () => menuState.openKeys,
  (_val, oldVal) => {
    menuState.preOpenKeys = oldVal
  },
)
</script>
ts
import { defineStore } from "pinia"
// 菜单数据
export const useMenuDataStore = defineStore('menuData', {
    // 真正存储数据的地方
    state() { 
        return {
            items: [
                {
                    key: '1',
                    label: 'dashboard',
                    title: '工作台',
                }
            ]
        }
    }
})

运行控制台可以看到一个菠萝🥭: Alt text

5. 修改数据的三种方式

5.1 直接修改

相比Vuex,Pinia支持直接修改

ts
countStore.sum = 666

5.2 批量修改

ts
countStore.$patch({
  sum: 999,
  school: '山东南翔'
})

5.3 借助action修改

ts
import { defineStore } from "pinia"
export const useCountStore  = defineStore('count', {
  state(){
    return {
      sum:100
    }
  }
  // actions中存放方法,用于响应组件中的"动作"
  actions:{
    // 加法逻辑
    increment(value:number){
      if(this.sum<10){
        // 操作countStore中的sum
        this.sum += sum
      }
    }
  }
})

6. storeToRefs

模版里面每次读取store数据不是很方便,使用对象解构可以方便读取,但是对属性需要增加响应式,如果使用toRefs,那么将store里面所有的属性都变成响应式: Alt text 我们只需要state中的数据变成响应式,推荐使用pinia提供的storeToRefs函数

ts
import { watch } from 'vue'
import { useMenuStateStore, useMenuDataStore } from "../store/menu"
import { useCurrentUserDataStore } from "../store/user"
import { storeToRefs } from "pinia"

const {openKeys, selectedKeys, preOpenKeys } = storeToRefs(useMenuStateStore())
const { items } = storeToRefs(useMenuDataStore())
const currentUserData = useCurrentUserDataStore()
import axios from 'axios'
const {data:menuData } = await axios.get(`api/getMenus?userId=${currentUserData.userId}`)
console.log(menuData)
// 刷新全局缓存
menuData.forEach((ele: { key: string; label: string; title: string; }) => {
  items.push(ele)
});

watch(
  () => openKeys,
  (_val, oldVal) => {
    preOpenKeys = oldVal
  },
)

7. 使用getters

  1. 概念:当state中的数据,需要经过处理后再使用时,可以使用getters配置
  2. 配置getters
ts
import { defineStore } from "pinia"
export const useCountStore  = defineStore('count', {
  state(){
    return {
      sum:100,
      school: '南翔'
    }
  },
  getters:{
    bigSum(state){
      return state.sum*10
    },
    updateSchool():string{
      return this.school = '山东南翔'
    }
  }

8 使用$subscribe监测数据

通过store的$subscribe()方法侦听state及其变化

vue
<template>
  <div style="width: 256px">
    <a-menu 
      v-model:openKeys="openKeys" 
      v-model:selectedKeys="selectedKeys" 
      mode="inline" 
      theme="dark"
      :inline-collapsed="collapsed" 
      :items="items">
    </a-menu>
  </div>
</template>
<script lang="ts" setup>
import { watch } from 'vue'
import { useMenuStateStore, useMenuDataStore } from "../store/menu"

const menuState = useMenuStateStore()
const {openKeys, selectedKeys, preOpenKeys } = storeToRefs(useMenuStateStore())
const { items } = storeToRefs(useMenuDataStore())

watch(
  () => openKeys,
  (_val, oldVal) => {
    preOpenKeys = oldVal
  },
)
menuState.$subscribe((mutate, state)=>{
  console.log('menuState里面保存的数据发生变化')
})
</script>