vue3-vuex持久化实现

一、背景描述

有时候我们可能需要在vuex中存储一些静态数据,比如一些下拉选项的字典数据。这种数据基本很少会变化,所以我们可能希望将其存储起来,这样就减少了请求的数量。
但是在每个位置都进行存储,好像是在做重复的逻辑,所以我们可以考虑将这个功能提取出来,作为一个插件使用。
注意:建议不要滥用持久化存储,因为这可能导致你不能获取到最新的数据,只建议在一些长期不会变化的数据中使用。

二、实现思路

做到持久化存储,那就要在页面销毁之前将值存到storage中,页面初始化的时候,再将值取到vuex中进行数据初始化

1.定义数据结构

我们首先要规定哪些值需要存储,因为我们没必要持久化存储大部分的vuex数据,所以没必要进行全量存储。
我这里将数据结构定义为数组:

const storageItem = [
  {
    
    
    storageType: 'local', // 存储类型:local或者session
    storageKey: 'name', // 存储的key
    storageValue: () => Store.getters.getName || '', // 需要存储的值,也可以用 () => Store.state.name 这种形式
    storeMutations: 'SET_NAME' // vuex设置值时的mutations,用于页面刷新完成之后初始化vuex
  }
]

每多一个需要持久化存储的内容,就增加一个元素即可。

2.存值

在页面销毁前存值我们可以直接在window的beforeunload回调中进行即可

window.addEventListener('beforeunload', () => {
    
    
      storageItem.forEach(item => {
    
    
        const storage =
          item.storageType === 'session' ? sessionStorage : localStorage
        storage.setItem(item.storageKey, item.storageValue())
      })
    })

也可以在vue组件的onUnmounted回调中进行存储,但是这样就需要你在vue组件中执行这个逻辑了。当然你也可以考虑将逻辑封装为hooks。

3.取值

在页面渲染前从storage中取到值,并且初始化vuex。
有一点可能要注意,我们从后端获取一些全局数据时,一般会在routerBeforeEach中进行接口调用。所以不建议在window的load回调中调用。我们执行初始化尽量在routerBeforeEach之前执行,这样我们就可以判断vuex如果存在值,就不用再调用接口了。
我这里在main.js中调用插件时执行:

storageItem.forEach(item => {
    
    
      const storage =
        item.storageType === 'session' ? sessionStorage : localStorage
      let storageValue = storage.getItem(item.storageKey)
      try {
    
    
        storageValue = JSON.parse(storageValue as string)
      } catch {
    
    }
      if (storageValue) {
    
    
        if (item.storeMutations) {
    
    
          Store.commit(item.storeMutations, storageValue)
        }
      }
    })

4.清空

我们可以提供一个清空的方法,便于某些时候清空所有的存储(如果担心数据时效性,可以设置一个时间,超出这个时间段之后就全部清空)

  storageItem.forEach(item => {
    
    
    const storage =
      item.storageType === 'session' ? sessionStorage : localStorage
    storage.removeItem(item.storageKey)
  })

三、具体代码

1.定义插件

新建一个storeStorage.js

import Store from '@/store'
/**
 * 统一移除存储的vuex数据
 */
export const removeStoreStorage = () => {
    
    
  storageItem.forEach(item => {
    
    
    const storage =
      item.storageType === 'session' ? sessionStorage : localStorage
    storage.removeItem(item.storageKey)
  })
}
// 持久化存储相应vuex数据
const storageItem = [
  {
    
    
    storageType: 'local', // 存储类型:local或者session
    storageKey: 'name', // 存储的key
    storageValue: () => Store.getters.getName || '', // 需要存储的值
    storeMutations: 'SET_NAME' // vuex设置值时的mutations,用于页面刷新完成之后初始化vuex
  }
]
export default {
    
    
  install() {
    
    
    this.getStoreStorage()
    this.setStoreStorage()
  },
  /**
   * 页面销毁前,存储数据
   */
  setStoreStorage() {
    
    
    window.addEventListener('beforeunload', () => {
    
    
      storageItem.forEach(item => {
    
    
        const storage =
          item.storageType === 'session' ? sessionStorage : localStorage
        storage.setItem(item.storageKey, item.storageValue())
      })
    })
  },
  /**
   * 页面刷新时,重新加载存储的vuex数据
   */
  getStoreStorage() {
    
    
    storageItem.forEach(item => {
    
    
      const storage =
        item.storageType === 'session' ? sessionStorage : localStorage
      let storageValue = storage.getItem(item.storageKey)
      try {
    
    
        storageValue = JSON.parse(storageValue as string)
      } catch {
    
    }
      if (storageValue) {
    
    
        if (item.storeMutations) {
    
    
          Store.commit(item.storeMutations, storageValue)
        }
      }
    })
  }
}

2.使用插件

main.js中引入,并使用

import {
    
     createApp } from 'vue'
import App from './App.vue'
import router from './router'
import storeStorage from '@/util/storeStorage'
import store from './store'
const app = createApp(App)
app.use(store).use(router).use(storeStorage).mount('#app')

其中vuex中index.js定义:

import {
    
     createStore } from 'vuex'

export default createStore({
    
    
  state: {
    
    
    name: '',
    age: ''
  },
  getters: {
    
    
    getName: state => state.name,
    getAge: state => state.age
  },
  mutations: {
    
    
    SET_NAME(state, name) {
    
    
      state.name = name
    },
    SET_AGE(state, age) {
    
    
      state.age = age
    }
  },
  actions: {
    
    },
  modules: {
    
    }
})

四、最终效果

app.vue

<template>
  <input type="text" v-model="$store.state.name"/>
</template>

<style lang="scss">
#app {
      
      
  color: #2c3e50;
}
</style>
<script setup lang="ts">
</script>

在这里插入图片描述
输入内容再刷新页面就会发现值被缓存了。

注:插件、routerBeforeEach和window.load执行顺序

router.beforeEach((to, from, next) => {
    
    
  console.log('routerBeforeEach')
  next()
})
window.addEventListener('load', () => {
    
    
  console.log('load')
})

插件中的部分代码

 /**
   * 页面刷新时,重新加载存储的vuex数据
   */
  getStoreStorage() {
    
    
    storageItem.forEach(item => {
    
    
      const storage =
        item.storageType === 'session' ? sessionStorage : localStorage
      let storageValue = storage.getItem(item.storageKey)
      try {
    
    
        storageValue = JSON.parse(storageValue as string)
      } catch {
    
    }
      if (storageValue) {
    
    
        if (item.storeMutations) {
    
    
          Store.commit(item.storeMutations, storageValue)
        }
      }
    })
    console.log('getStoreStorage')
  }

执行结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43845090/article/details/132584299
今日推荐