Vuex持久化插件

4,152 阅读2分钟

前言

用vue开发中大型应用时候,我们通常都会使用vuex进行状态管理,但刷新后刷新后存在内存中的vuex数据将会丢失。在很多场景中,我们不愿意看到这样的结果的。

Vuex 社区中有相关的解决方案,比如vue-savedata,vuex-persistedstate,解决思路都是state里的数据保存一份到本地存储(localStorage、sessionStorage、cookie)中。

实现插件之前,先看下怎么实现一个简单的插件

写个 Vuex 插件

Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数:

const myPlugin = store => {
  // 当 store 初始化后调用
  store.subscribe((mutation, state) => {
    // 每次 mutation 之后调用
    // mutation 的格式为 { type, payload }
  })
}

使用:

const store = new Vuex.Store({
  // ...
  plugins: [myPlugin]
})

store.subscribe : 订阅 store 的 mutation。handler 会在每个 mutation 完成后调用,接收 mutation 和经过 mutation 后的状态作为参数

实现一个简单的Vuex持久缓存插件

缺点:vuex 中所有的state全部保存在本地storage中,并且每次触发mutation,都会储存state。如果频繁操作vuex,会一直更新storage,性能太差


export default (options = {}) => {
    return store => {
        store.replaceState(JSON.parse(window.localStorage.cacheStore))
        store.subscribe((mutation,state) => {
            window.localStorage.setItem('cacheStore',JSON.stringify(state))
        })
    }
}

设计一个完整的Vuex持久缓存插件

实现代码

export default (options = {}) => {
    /**
        cache:缓存列表
        delay:防抖或节流时间
        mode:防抖或节流(debounce|throttle )自行实现吧(^_^)
    */
    let {cache = [],delay = 1000,mode='debounce'} = options
    
    // 缓存的数据
    let cacheData = {}
    
    // 本地储存辅助函数 mode:缓存模式
    const storeage = (mode = "LS") => {
        if(mode.toUpperCase() == 'SS') {
            return window.sessionStorage
        }
        return window.localStorage
    }
    
    // 防抖函数
    const debounce = (fn, delay) => {
    	let timer = null;
    	let status = true;
    	return function () {
    		let args = arguments;
    		if (timer) clearTimeout(timer)
    		timer = setTimeout(() => fn.apply(this, args), delay)
    	}
    }
    
    // 判断是否对象
    const isObject = value => Object.prototype.toString.call(value) == "[object Object]"
    
    // 合并对象
    const merge = (a,b) => {
        for (var key in b) {
            if (!a.hasOwnProperty(key)) {
                a[key] = b[key];
            } else if (isObject(b[key]) && isObject(a[key])) {
                merge(a[key], b[key]);
            }
        }
        return a;
    }
    
    // 窗口关闭触发更新本地storeage的数据
    window.addEventListener('beforeunload',() => {
        Object.keys(cacheData).forEach(key => {
            let {mode,...other} = cacheData[key]
            storeage(mode).setItem(key,JSON.stringify(other))
        })
    },false)
    
    return store => {
        let {state:initState,_modulesNamespaceMap} = store
        // 判断是否模块
        const hasModule = name => !!_modulesNamespaceMap[name+'/']
        
        let data = cache.reduce((prev,curr) => {
            let {key,mode} = curr
            if(hasModule(key) && storeage(mode)[key]){
                prev[key] = JSON.parse(storeage(mode)[key]).data
            }else{ // Root state
                // 需要的可以自行扩展
                // code
            }
            return prev
        },{})
        
        //合并本地storeage中的数据到Vuex state 中,并初始化
        merge(data,initState)
        store.replaceState(data)
        
        store.subscribe(debounce((mutation,state) => {
            let [key,type] = mutation.split('/')
            // 是否模块
            if(type && hasModule(key)){ 
                let findItem = cache.find(option => option.key === key)
                //是否缓存
                if(findItem){
                    let {whiteList,mode} = findItem
                    // 是否在缓存白名单中
                    if(whiteList && !whiteList.includes(type) || !whiteList){
                        let module = state[key]
                        cacheData[key] = {
                            mode,
                            data:module
                        }
                    }
                }
            }else{ // Root state
                // 需要的可以自行扩展
                // code
            }
        },delay))
    }
}

使用插件

import CachePlugin from './vuex-cache-plugin'
const store = new Vuex.Store({
  modules:{
      saveData:{
          namespaced:true
      },
      saveData2:{
          namespaced:true
      }
  }
  plugins: [
    CachePlugin({
        delay:1000,
        cache:[
            {key:'saveData',mode:'SS'}
            {key:'saveData2',whiteList:['SET_TEST']}
        ]
    })
  ]
})