前言
用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']}
]
})
]
})