Skip to content

第三章:状态管理

介绍

框架使用的状态管理工具是 pinia ,它允许我们跨页面/跨组件进行状态共享,在 Pinia 中,核心概念是 store。每个 store 就像一个独立的状态容器。框架中存放位置于 src/store 目录中,在 src/store/index.js 文件中定义了框架中所有使用 pinia 的对象。

特别注意

当页面使用F5刷新的时候,pinia 中保存的状态是不会被保留的,页面刷新会重新加载整个 javascript 环境,因此 Pinia store 会重置为其初始状态

定义和使用

定义

使用函数 defineStore 来创建一个 store , 第一个参数 counterstore 的唯一ID,第二个参数是一个选项对象,包含:

  • state: 定义store的初始状态
  • getters: 类似于计算属性,用于派生状态
  • actions: 包含可以改变状态的方法
javascript
import { defineStore } from 'pinia'
// 定义
const useCounterStore = defineStore('counter', {
  state: () => {
    count: 0,
  },
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment (name) {
      this.count++
    }
  }
})

使用

使用 useCounterStore 函数来获取 store 实例

  • 可以直接访问 state (counter.count)
  • 可以直接调用 actions (counter.increment())
  • Getters 的使用方式和 state 相同
vue
<script setup>
import { useCounterStore  }from "@/stores/counter"

// 获取store实例
const counter = useCounterStore()

// 方法
function handleClick(){
  counter.increment()
}
</script>

<template>
  <div>
    <p>count:{{ counter.count }}</p>
    <p>Double count:{{ counter.doubleCount }}</p>
    <button @click="handleClick">Increment</button>
  </div>
</template>

框架中使用实例

saiadmin 框架中,我们有一项设计是系统的字典数据使用,因为数据字典是允许跨页面、跨组件访问的,所以很适合采取 pinia 状态进行保存,我们初步设计在用户登录成功获取基本数据和配置后,加载系统的字典数据,下面来介绍详细的使用步骤。

  1. 定义字典数据的 store, 首先创建文件 src/store/modules/dict.js, 创建内容如下:
javascript
import { defineStore } from 'pinia'
import commonApi from '@/api/common'

// 定义字典store,名称是dict
const useDictStore = defineStore('dict', {
  // 字典数据是数组,我们定义一个data来进行保存
  state: () => ({ data: undefined }),
  getters: {
    // 获取store状态
    getState() {
      return { ...this.$state }
    }
  },
  actions: {
    //给字典数据赋值
    setInfo(data) {
      this.$patch(data)
    },
    // 初始化字典数据
    async initData() {
      const { data } = await commonApi.dictAll()
      this.data = data
    }
  }
})

export default useDictStore
  1. 将字典 store 进行引用,引入后我们就可以在其他页面进行调用了,引用 store 的文件是 src/store/index.js
javascript
import { createPinia } from 'pinia'
import useUserStore from './modules/user'
import useAppStore from './modules/app'
import useTagStore from './modules/tag'
import useKeepAliveStore from './modules/keepAlive'
import useIframeStore from './modules/iframe'
import useConfigStore from './modules/config'
import useMessageStore from './modules/message'
// 引入字典 store
import useDictStore from './modules/dict'

const pinia = createPinia()

export {
  useUserStore,
  useAppStore,
  useTagStore,
  useKeepAliveStore,
  useIframeStore,
  useConfigStore,
  useMessageStore,
  useDictStore // 对外暴露字典 store
}
export default pinia
  1. 在用户数据加载成功后,写入字典数据到 store 中,在文件 src/store/modules/user.js 中如下
javascript
requestUserInfo() {
      return new Promise((resolve, reject) => {
        loginApi.getInfo().then(async (response) => {
          if (!response || !response.data) {
            this.clearToken()
            await router.push({ name: 'login' })
            reject(false)
          } else {
            this.setInfo(response.data)
            // 初始化字典数据
            const dictStore = useDictStore()
            await dictStore.initData()
            homePage.children = webRouter[0].children
            this.setMenu(this.routers)
            this.routers = removeButtonMenu(this.routers)
            this.routers.unshift(homePage)
            await this.setApp()
            resolve(response.data)
          }
        })
      })
    },
  1. 页面或者组件中进行调用,例如我们封装的组件 sa-radio 中,我们通过传递字典名称,就能渲染对应字典的数据,是因为在这个页面中, 我们通过 store 获取了字典列表,然后在字典列表中拿到了字典数据
vue
<template>
  <a-radio-group v-model="value" :direction="props.direction" :type="props.type" :disabled="props.disabled" @change="handleChangeEvent($event)">
    <template v-for="(item, index) in dictList[props.dict] ?? []">
      <a-radio :value="item.value">{{ item.label }}</a-radio>
    </template>
  </a-radio-group>
</template>

<script setup>
import { ref, watch } from 'vue'
// 引用字典store
import { useDictStore } from '@/store'

// 定义字典示例
const dictList = useDictStore().data
const emit = defineEmits(['update:modelValue', 'change'])
const value = ref()

const props = defineProps({
  modelValue: { type: [String, Number] },
  type: { type: String, default: 'radio' },
  dict: { type: String, default: '' },
  disabled: { type: Boolean, default: false },
  direction: { type: String, default: 'horizontal' },
})
</script>

基于 MIT 许可发布.