背景
最近用uni-app开发小程序项目时,部分需要持久化的内容没法像其他vuex中的state那样调用,所以想着自己实现一下类似vuex-persistedstate插件的功能,貌似代码量也不会很大
初步思路
首先想到的实现方式自然是vue的watcher模式。对需要持久化的内容进行劫持,当内容改变时,执行持久化的方法。
先弄个dep和observer,直接observer需要持久化的state,并传入get和set时的回调:
function dep(obj, key, options) {
let data = obj[key] Object.defineProperty(obj, key, {
configurable: true,
get() {
options.get()
return data
},
set(val) {
if (val === data) return
data = val
if(getType(data)==='object') observer(data)
options.set()
}
})
}
function observer(obj, options) {
if (getType(obj) !== 'object') throw ('参数需为object')
Object.keys(obj).forEach(key => {
dep(obj, key, options)
if(getType(obj[key]) === 'object') {
observer(obj[key], options)
}
})
}然而很快就发现问题,若将a={b:{c:d:{e:1}}}存入storage,操作一般是xxstorage(‘a’,a),接下来无论是改了a.b还是a.b.c或是a.b.c.d.e,都需要重新执行xxstorage(‘a’,a),也就是无论a的哪个后代节点变动了,重新持久化的都是整个object树,所以监测到某个根节点的后代节点变更后,需要先找到根节点,再将根节点对应的项重新持久化。
接下来的第一个问题就是,如何找到变动节点的父节点。
state树的重新构造
如果沿着state向下找到变动的节点,并根据找到节点的路径确认变动项,复杂度太高。
如果在observer的时候,对state中的每一项增添一个指向父节点的指针,在后代节点变动时,是不是就能沿着指向父节点的指针找到相应的根节点了?
为避免新增的指针被遍历到,决定采用Symbol,于是dep部分变动如下:
function dep(obj, key, options) {
let data = obj[key] if (getType(data)==='object') {
data[Symbol.for('parent')] = obj
data[Symbol.for('key')] = key
}
Object.defineProperty(obj, key, {
configurable: true,
get() {
...
},
set(val) {
if (val === data) return
data = val
if(getType(data)==='object') {
data[Symbol.for('parent')] = obj
data[Symbol.for('key')] = key
observer(data)
}
...
}
})
}
再加个可以找到根节点的方法,就可以改变对应storage项了
function getStoragePath(obj, key) {
let storagePath = [key] while (obj) {
if (obj[Symbol.for('key')]) {










