如何实现一个简易版的vuex持久化工具

  


  

  

最近用uni-app开发小程序项目时,部分需要持久化的内容没法像其他vuex中的状态那样调用,所以想着自己实现一下类似vuex-persistedstate插件的功能,貌似代码量也不会很大

  


  

  

首先想到的实现方式自然是vue的观察家模式。对需要持久化的内容进行劫持,当内容改变时,执行持久化的方法。
  先弄个dep和观察者,直接观察者需要持久化的状态,并传入获取和设置时的回调:

        函数dep (obj,关键选项){   让数据=https://www.yisu.com/zixun/obj(例子)   Object.defineProperty (obj,钥匙,{   可配置:没错,   get () {   options.get ()   返回数据   },   集(val) {   如果(val===数据)回来   data=https://www.yisu.com/zixun/val   如果(方法(数据)==='对象')观察者(数据)   options.set ()   }   })   }   函数观测器(obj,选项){   如果方法(obj) !==岸韵蟆?把(“参数需为对象”)   种(obj)。forEach(关键=比;{   部(obj,关键选项)   如果方法(obj(例子))==='对象'){   观察者(obj(关键)选项)   }   })   }      

然而很快就发现问题,若将一个={b: {c: d: {e: 1}}}存入存储、操作一般是xxstorage (' a ',),接下来无论是改了a.b还是a.b.c或是a.b.c.d.e,都需要重新执行xxstorage (' a ',),也就是无论一个的哪个后代节点变动了,重新持久化的都是整个对象树,所以监测到某个根节点的后代节点变更后,需要先找到根节点,再将根节点对应的项重新持久化。
  

  

接下来的第一个问题就是,如何找到变动节点的父节点。

  


  

  

如果沿着向状态下找到变动的节点,并根据找到节点的路径确认变动项,复杂度太高。
  

  

如果在观察者的时候,对状态中的每一项增添一个指向父节点的指针,在后代节点变动时,是不是就能沿着指向父节点的指针找到相应的根节点了?
  

  

为避免新增的指针被遍历到,决定采用符号,于是dep部分变动如下:

        函数dep (obj,关键选项){   让数据=https://www.yisu.com/zixun/obj(例子)   如果(方法(数据)==='对象'){   数据(Symbol.for(“母公司”)]=obj   数据(Symbol.for(关键)]=键   }   Object.defineProperty (obj,钥匙,{   可配置:没错,   get () {   …   },   集(val) {   如果(val===数据)回来   data=https://www.yisu.com/zixun/val   如果(方法(数据)==='对象'){   数据(Symbol.for(“母公司”)]=obj   数据(Symbol.for(关键)]=键   观察者(数据)   }   …   }   })   }   之前      

再加个可以找到根节点的方法,就可以改变对应存储项了

        函数getStoragePath (obj,键){   让storagePath=(例子)   而(obj) {   如果(obj [Symbol.for(关键)]){   关键=obj [Symbol.for(关键)]   storagePath.unshift(关键)   }   obj=obj [Symbol.for(“母公司”)]   }//storagePath[0]就是根节点,storagePath记录了从根节点到变动节点的路径   返回storagePath   }   之前      

但是问题又来了,对象是可以实现自动持久化了,数组用推,流行这些方法操作时,数组的地址是没有变动的,defineProperty根本监测不到这种地址没变的情况(可惜代理兼容性太差,小程序中安卓直接不支持)。当然,每次操作数组时,对数组重新赋值可以解决此问题,但是用起来太不方便了。

  


  

  

数组的问题,解决方式一样是参照vue源码的处理,重写数组的“推动”,“流行”、“转移”、“平移”,“接头”,“排序”,“反向”方法
  数组用这7种方法操作数组的时候,手动触发组中部分,更新存储内容

  

添加防抖
  

  

vuex持久化时,容易遇到频繁操作状态的情况,如果一直更新存储、性能太差

  

<>强实现代码
  

  

最后代码如下:
  

  

tool.js:

     /*   持久化相关内容   *///重写数组的方法   const funcArr=[“推”、“流行”,“转变”,“平移”,“接头”,“排序”,“反向”)   const typeArr=(“对象”、“数组”)      函数setCallBack (obj,键,选择){   如果选择,,options.set) {   如果方法(options.set) !==昂?把(“options.set需为函数”)   选项。集(obj,键)   }   }      函数rewriteArrFunc(加勒比海盗、期权){   如果方法(arr) !==笆椤?把(“参数需为数组”)   funcArr。forEach(关键=比;{   arr(例子)=函数(…args) {   this.__proto__(关键)。调用(这…args)   setCallBack(这个[Symbol.for(“母公司”)],这[Symbol.for(关键)],选项)   }   })   }      函数dep (obj,关键选项){   让数据=https://www.yisu.com/zixun/obj(例子)   如果(typeArr.includes(方法(数据))){   数据(Symbol.for(“母公司”)]=obj   数据(Symbol.for(关键)]=键   }   Object.defineProperty (obj,钥匙,{   可配置:没错,   get () {   如果选择,,options.get) {   选项。get (obj,键)   }   返回数据   },   集(val) {   如果(val===数据)回来   data=https://www.yisu.com/zixun/val   让指数=typeArr.indexOf(方法(数据)   如果(指数>=0){   数据(Symbol.for(“母公司”)]=obj   数据(Symbol.for(关键)]=键   如果(索引){   rewriteArrFunc(数据选项)   其他}{   观察者(数据、选择)   }   }   setCallBack (obj,关键选项)   }   })   }      函数观测器(obj,选项){   如果方法(obj) !==岸韵蟆?把(“参数需为对象”)   让指数   种(obj)。forEach(关键=比;{   部(obj,关键选项)   指数=typeArr.indexOf(方法(obj(例子)))   如果(指数& lt;0)返回   如果(索引){   rewriteArrFunc (obj(例子),选项)   其他}{   观察者(obj(关键)选项)   }   })   }   函数debounceStorage (fn,延迟){   如果方法(fn) !==昂?返回null   让updateItems=new ()   让计时器=零   返回函数setToStorage (obj,键){   让changeKey=getStoragePath (obj,键)[0]   updateItems.add (changeKey)   clearTimeout(计时)   计时器=setTimeout(()=比;{   尝试{   updateItems。forEach(关键=比;{   fn。调用(关键,这状态(例子))   })   updateItems.clear ()   }捕捉(e) {   console.error (“persistent.js中国家内容持久化失败,错误位于[$ {changeKey}]参数中的[${关键}]项”)   }   },延迟)   }   }   导出功能getStoragePath (obj,键){   让storagePath=(例子)   而(obj) {   如果(obj [Symbol.for(关键)]){   关键=obj [Symbol.for(关键)]   storagePath.unshift(关键)   }   obj=obj [Symbol.for(“母公司”)]   }   返回storagePath   }   导出功能persistedState({状态、setItem getItem setDelay=0, getDelay=0}) {   观察者(状态,{   设置:debounceStorage(状态、setItem setDelay),   得到:debounceStorage(状态、getItem getDelay)   })   }/*   vuex自动配置突变相关方法   */导出功能setMutations (stateReplace mutationsReplace) {   种(stateReplace)。forEach(关键=比;{   让name=键。替换(\ w/(第一次)=祝辞null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null   null

如何实现一个简易版的vuex持久化工具