文章目录
- 使用
- 插件编写
- 实现
使用
关于插件的使用文档: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信息:
就和 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
使用为例,我们主要需要实现的两个方法是subscribe
和replaceState
。
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。
产生原因是因为我们在replaceState 中虽然进行了替换,但是在installModule
中之前添加的gettres
,actions
,mutations
等都还是用的之前传的state
所以之前的逻辑就需要进行一些调整,每次 都应该获取最新的state。在store.js
中新建一个方法
// 通过 path 路径 去 store 里去找最新的状态
function getNewState (store, path) {
return path.reduce((memo, key) => {
return memo[key]
}, store.state)
}
然后在installModule
中作如下修改:
完整代码:https://gitee.com/Nsir/my-vue/blob/master/vuex-project/src/vuex/store.js