優雅的在Vue3中使用Vuex「TypeScript版」

Seale_極束夢想發表於2021-01-24

前言

我們都知道,vuex 的使用在專案中頗為繁瑣,因為它有幾大概念使得它不能像普通 ref 或者 data 物件一樣直接被我們使用,在我們想要更改 vuex 中的資料時,我們需要透過 mutation 來進行提交,獲取 vuex 中儲存的變數的時候,我們又需要透過 computed 屬性來進行宣告,試想,如果專案足夠龐大,那麼我們使用 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 內加入如下程式碼(在這裡參考了 uview 封裝的 vuex 方法 )

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')
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章