手写Vuex持久化插件

297 阅读2分钟

一、前置知识

1. vuex的plugins选项

  • 该选项接收一个数组,数组每一项是一个函数(也就是一个插件)
  • 项目初始化后,数组的每一项,会依次执行一次,且接收store
import { createStore } from 'vuex'
export default createStore({
  // ......
  plugins: [fn1, fn2]  // store初始化后,fn1和fn2会依次执行
})

function fn1(store){
  console.log(store) // 函数会接收store
}
function fn2(store) {
  console.log(store)
}

2. store.subscribe方法监听mutation

  • 该方法接收一个回调函数作为参数
  • 只要mutation中的方法执行,就会执行该回调函数
  • 该回调函数会接收两个参数,一个是当前执行mutation,一个是state
import { createStore } from 'vuex'
import modules1 from './modules/modules1'
export default createStore({
  // ......
  mutations: {
    ADDCOUNT: (state,data) => {
      state.count = data
    }
  },
  modules: {
    modules1
  },
  plugins: [fn1]
})
function fn1(store){
  store.subscribe((mutations,state) => {
    console.log(mutations, state) // 回调函数会接收mutation和state 
  })
}

3. store.replaceState替换旧的state

  • 由于state是响应式数据,不能直接赋值,所以使用replaceState方法修改state
store.replaceState(newState)

二、实现简易vuex持久化插件

思路:

  1. 在插件中监听mutation执行,mutation执行时设置缓存
  2. 刷新时,将缓存中的数据替换到state中

代码:

// 使用
import { createStore } from 'vuex'
import modules1 from './modules/modules1'
export default createStore({
  modules: {
    modules1
  },
  plugins: [ persistencePlugin ]
})

// 插件函数
function persistencePlugin(store) {
  // 初始化(刷新)时,取缓存,设置state
  const storage = JSON.parse(sessionStorage.getItem('state'))
  if (storage) {
    store.replaceState(storage)
  }
  // 监听mutation执行,存缓存
  store.subscribe((mutation, state) => {
    sessionStorage.setItem('state', JSON.stringify(state))
  })
}

三、自定义vuex持久化内容

支持定义:

  1. 需要持久化的内容
  2. 缓存key名称
  3. session还是local缓存

插件代码:

/*
 * watch:需要监听的数据,默认监听全部
 * keyName:存储缓存的key名称,默认'vuex'
 * storage:缓存类型,默认sessionStorage
 */
export function persistencePlugin(customConfig) {
  let {
    watch = '*',
    keyName = 'vuex',
    storage = 'sessionStorage'
  } = customConfig
  // 返回一个函数
  return function (store) {
    const data = JSON.parse(window[storage].getItem(keyName))
    if (data) {
      if (watch == '*') {
        store.replaceState(data)  // 监听全部,直接替换state
      } else {
        // 只修改部分state
        setObject(store.state, watch, data)
      }
    }
    // 监听state变化,存缓存
    store.subscribe((mutation, state) => {
      if (watch == '*') {
        // watch监听全部,全部存缓存,使用自定义的keyName
        window[storage].setItem(keyName, JSON.stringify(state))
      } else {
        // watch监听部分,部分存缓存
        window[storage].setItem(
          keyName,
          JSON.stringify(parseObject(store.state, watch))
        )
      }
    })
  }
}

// 解析对象的值,key是a.b.c 这样的格式
function parseObject(obj, keyString) {
  if (!keyString) return
  const keys = keyString.split('.')
  return keys.reduce((obj, key) => obj[key], obj)
}

// 设置对象的值,key是a.b.c格式时候
function setObject(obj, keyString, newDate) {
  if (!keyString) return
  const keys = keyString.split('.')
  const lastIndex = keys.length - 1
  keys.forEach((key, index) => {
    if (index === lastIndex) {
      // 最后一个索引,给对象赋值
      obj[key] = newDate
    } else {
      // 不是最后一个所有,给obj重新赋值
      obj = obj[key]
    }
  })
}

使用:

import { createStore } from 'vuex'
import modules1 from './modules/modules1'
export default createStore({
  modules: {
    modules1
  },
  plugins: [
    persistencePlugin({
      watch: 'modules1.modulesCount', // 持久化模块modules1中modulesCount的值
      keyName: 'modulesCount',    // 存储的keyname
      storage: 'localStorage'     // 使用本地存储
    })
  ]
})

效果:

若还需要其他自定义功能,可自行修改