前言

我们都知道,vuex 的使用在项目中颇为繁琐,因为它有几大概念使得它不能像普通 ref 或者 data 对象一样直接被我们使用,在我们想要更改 vuex 中的数据时,我们需要通过 mutation 来进行提交,获取 vuex 中存储的变量的时候,我们又需要通过 computed 属性来进行声明,试想,如果项目足够庞大,那么我们使用 vuex 的负担就过于重了,这违背了我们使用状态管理的本意。

这里所指的项目足够庞大说的是页面组件逻辑过于复杂,多层嵌套,并不是说在Vuex中存储了大量数据,在Vuex存储变量请遵循Vuex的设计准则,存储一些多层组件需要通信的值

问题的提出

首先,我们需要明确,我们想要怎么快捷便利地来使用 vuex ,如果你的想法和我如下所示相同,那么恭喜你,或许本文提出的解决方案适合你。

存储

对于 vuex 的存储我们想要实现的类似如下:

this.$m.vuex(name,value);

我们可以在各个组件或者视图内,通过this来直接使用 vuex 进行存储。

读取

对于 vuex 中数据的读取我们想要实现的类似如下:

<template>
<div>{{vuexUser.name}}</div>
</template>

<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
    ...
    methods:{
        // 业务方法
        xxxxx(){
            const flag = this.vuexUser.name? true:false;
            .....
        }
  }
})
</script>

我们能够在模板或者业务逻辑中直接通过 this 直接访问。

解决方案

首先,我们需要在 store 目录下的 index.ts 内加入如下代码

import { createStore } from 'vuex'

export default createStore({
  state: {
    vuexIsLogin: false,        // 当前的登录状态
    vuexTestVar: "这是vuex全局混入的测试变量",
    vuexUser:{
      test: 1,
      name: "测试名字"
    }
  },
  mutations: {
    $changeStore(state: any,payload: any){
      // 判断是否为多层级调用,state中为对象存在的情况,诸如user.info.name = 'xxx'
      const nameArr = payload.name.split('.');
      const len = nameArr.length;
      if (len >= 2){

        let obj = state[nameArr[0]];
        for (let i = 1 ; i < len - 1 ; i++){
          obj = obj[nameArr[i]];
        }
        obj[nameArr[len-1]] = payload.value;
      }else {
        state[payload.name] = payload.value;
      }
    }
  }
})

之后我们在 store 目录下创建一个 ts 脚本文件,在我的项目中我命名为 maxer.mixin.ts ,在这里,我们就需要使用Vue中的一个特性 Mixin(混入)「不明白的同学可以去官方文档查一查,Vue3的官方中文版已经推出了」

/**
 * @作者: Seale
 * @时间: 2021/1/23
 * @版本: V1.0
 * @说明: maxer Vue 全局混入
 * @网站: https://www.imsle.com
 */

import {mapState} from "vuex";
import store from '@/store/index.ts'
import {App} from 'vue'

// 将定义的state变量key全部加载到全局变量中
const $mStoreKey = store.state ? Object.keys(store.state) : [];
export class Maxer{
    vuex = (name: string, value: any): void=>{
        store.commit('$changeStore', {
            name, value
        })
    }
}

export default<T> (app: App<T>) => {
    // 进行全局混入
    // 将vuex方法挂载到$m中
    // 使用方法为:如果要修改vuex的state中的user.name变量为"x" => this.$m.vuex('user.name','x')
    app.config.globalProperties.$m = new Maxer();
    app.mixin({
        computed: {
            // 将vuex的state中的所有变量,解构到全局混入的mixin中
            ...mapState($mStoreKey)
        }
    })
}

在这里我们使用全局混入,将 vuex 的 state 混入到计算属性中,我们就可以通过类似 this.vuexName 来进行调用,还有一点建议就是,由于采用全局混入的模式将 vuex 中的数据进行混入,所以我们应该用特定的字符来进行标识 vuex 中的数据,我的建议是在 vuex 中变量前加上 vuex_ prefix ,或者类似 vuexVar:'xxx' 来进行声明变量。

之后我们就需要在 main.ts 中进行初始化了。

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
import installMaxerStore, {Maxer} from './store/maxer.mixin'
...

// 声明全局组件 防止需要this调用时不能识别类型
declare module '@vue/runtime-core' {
    interface ComponentCustomProperties {
        $m: Maxer;  // 声明全局方法
    }
}
...
const app = createApp(App)
installMaxerStore(app) // 全局混入vuex
app.use(store).mount('#app')

之后我们就可以愉快地使用 vuex 了(在注意变量命名的情况下)。

Vuex 数据持久化

这个时候或许你会发现,当页面进行刷新的时候,vuex 的数据会进行初始化(回到最初的状态),这个是由于 vuex 是运行在内存中的,同样的,它的数据也是存储在内存中,当用户进行刷新页面的操作,所以内存数据会重新进行初始化。

那么我们就可以通过 sessionStorage / localStorage / cookie 来进行数据的持久化存储。

页面加载的时候先读取域中的缓存数据,如果有则覆写。当页面将要刷新前,我们将 vuex 的数据存储到域中。

这里建议使用 sessionStorage ,对于需要长时间持久化的数据再使用 localStorage 或者 cookie。

同样我们在 store 目录下新建一个 ts 脚本。

/**
 * @作者: Seale
 * @时间: 2021/1/24
 * @版本: V1.0
 * @说明: vuex数据持久化,防止F5之后数据消失
 * @网站: https://www.imsle.com
 */
import {Store} from "vuex";

export default<T> (store: Store<T>): void=>{
    // 不需要持久化的数据存入sessionStorage
    if (sessionStorage.getItem('store')){
        store.replaceState(
            Object.assign(
                {},
                store.state,
                JSON.parse(sessionStorage.getItem('store') as string)
            )
        );
        // 移除sessionStorage中的数据
        sessionStorage.removeItem("store");
    }
    // 页面刷新的时候进行持久化
    window.addEventListener('beforeunload',()=>{
        sessionStorage.setItem("store", JSON.stringify(store.state));
    })
}

对于需要长时间持久化的数据建议自行定义规则进行封装。

之后我们需要在入口文件中进行声明

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
import installMaxerStore, {Maxer} from './store/maxer.mixin'
import initStorePersistence from './store/store.persistence'
...

// 声明全局组件
declare module '@vue/runtime-core' {
    interface ComponentCustomProperties {
        $m: Maxer;  // 声明全局方法
    }
}
...
const app = createApp(App)
installMaxerStore(app) // 全局混入vuex
initStorePersistence(store) // 初始化持久化vuex
app.use(store).mount('#app')

本作品采用知识共享署名 4.0 国际许可协议进行许可。

如果可以的话,请给我钱请给我点赞赏,小小心意即可!

Last modification:June 24, 2021
If you think my article is useful to you, please feel free to appreciate