文章目录

  • 使用
  • 插件编写
  • 实现


使用

关于插件的使用文档:https://vuex.vuejs.org/zh/guide/plugins.html

以vue内置的logger 插件为例,在 store/index.js中引入:

import logger from 'vuex/dist/logger.js'
export default new Vuex.Store({
  plugins: [
    logger()
  ],
  state: {
    name: 'MRNAN',
    age: 30
  },
  mutations: {
    changeName (state, val) {
      state.name = val
    }
  },
  actions: {
    asyncChangeName ({ commit }, val) {
      setTimeout(() => {
        commit('changeName', 'asyncName')
      }, 1000)
    }
  },
}

然后在页面进行 commit或者 dispatch修改数据的时候,就会在控制台看到logger信息:

vue 显示java实时日志_vue 显示java实时日志


就和 chrome浏览器的 vue devtools 插件差不多。

vuex的状态在每次浏览器刷新的时候都会被清除掉,所以也也可以利用 vuex持久化插件来解决:·vuex-persistedstate·

插件编写

Vuex 插件就是一个函数,它接收 store 作为唯一参数。

自己实现一下 logger

function logger () {
  return function (store) {
    let prevState = store.state
    // 当 store 初始化后调用
    store.subscribe((mutation, state) => {
      // 每次 mutation 之后调用,如果是直接手动更改state,subscribe 不会被执行到
      console.log('prevState:', JSON.stringify(prevState))
      console.log(JSON.stringify(mutation))
      console.log('currentState:', JSON.stringify(state))
      prevState = JSON.stringify(state)
    })
  }
}

实现简易的持久化缓存

function persistent () {
  return (store) => {
    const localState = JSON.parse(localStorage.getItem('VUEX:PERSIS'))
    if (localState) {
      store.replaceState(localState)
    }

    store.subscribe((mutation, rootState) => {
      // 每次数据变化就存储
      // 这里调用会很频繁,需要添加防抖
      localStorage.setItem('VUEX:PERSIS', JSON.stringify(rootState))
    })
  }
}

实现

persistent使用为例,我们主要需要实现的两个方法是subscribereplaceState
vuex 的插件是函数,调用时机是每次store初始化的时候。subscribe的调用时机是每次mutation调用的时候。

store.js

在store初始化的时候,class Store

// 如果用户使用了plugins
if(options.plugins){
   options.plugins.forEach(plugin=>plugin(this))
 }

添加 subscribe 成员方法,subscribe 方法内部主要是收集,等到mutation执行的时候执行收集的fn。声明变量 this._subscribes = []

class Store{
	constructor(options){
		.... 其他逻辑
		this._subscribes = []
	}
	.... 其他逻辑
	subscribe(fn){
		this._subscribes.push(fn) // 然后等到 mutation 执行时候再去遍历依次执行fn
	}
}

然后在 installModule函数中,找到forEachMutation,在回调函数中执行 _subscribes里的fn,根据文档我们知道,fn接收两个参数,第一个是mutation,第二个是state,其中mutation的格式是{ type, payload }

module.forEachMutations((mutation, key) => {
    store.mutations[ns + key] = store.mutations[ns + key] || []
    store.mutations[ns + key].push((payload) => {
      mutation.call(store, module.state, payload)

      // subscribe中的mutation 的格式为 { type, payload }
      store._subscribes.forEach(fn => fn({ type: ns + key, payload }, state))
    })
  })

接下来实现 replaceState,replaceState就是直接粗暴的全量替换:

replaceState (newState) {
    this._vm._data.$$state = newState
  }

现在看起来好像是可以持久化存储了。不过产生了一个新的问题,就是刷新之后,再次点击更改年龄,localstorage中的值虽然一直变,但是界面并没有刷新,年龄还是 40。

vue 显示java实时日志_初始化_02


产生原因是因为我们在replaceState 中虽然进行了替换,但是在installModule中之前添加的gettres,actions,mutations等都还是用的之前传的state

vue 显示java实时日志_vue 显示java实时日志_03

所以之前的逻辑就需要进行一些调整,每次 都应该获取最新的state。在store.js中新建一个方法

//  通过 path 路径 去 store 里去找最新的状态
function getNewState (store, path) {
  return path.reduce((memo, key) => {
    return memo[key]
  }, store.state)
}

然后在installModule中作如下修改:

vue 显示java实时日志_vue 显示java实时日志_04

完整代码:https://gitee.com/Nsir/my-vue/blob/master/vuex-project/src/vuex/store.js