国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

如何實現一個簡易版的vuex持久化工具

2019-11-19 10:50:09
字體:
來源:轉載
供稿:網友

背景

最近用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')]) {   key = obj[Symbol.for('key')]   storagePath.unshift(key)  }  obj = obj[Symbol.for('parent')] } // storagePath[0]就是根節點,storagePath記錄了從根節點到變動節點的路徑 return storagePath }

但是問題又來了,object是可以實現自動持久化了,數組用push、pop這些方法操作時,數組的地址是沒有變動的,defineProperty根本監測不到這種地址沒變的情況(可惜Proxy兼容性太差,小程序中安卓直接不支持)。當然,每次操作數組時,對數組重新賦值可以解決此問題,但是用起來太不方便了。

改變數組時的雙向綁定

數組的問題,解決方式一樣是參照vue源碼的處理,重寫數組的'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'方法
數組用這7種方法操作數組的時候,手動觸發set中部分,更新storage內容

添加防抖

vuex持久化時,容易遇到頻繁操作state的情況,如果一直更新storage,性能太差

實現代碼

最后代碼如下:

tool.js:

/*持久化相關內容*/// 重寫的Array方法const funcArr = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']const typeArr = ['object', 'array']function setCallBack(obj, key, options) { if (options && options.set) {  if (getType(options.set) !== 'function') throw ('options.set需為function')  options.set(obj, key) }}function rewriteArrFunc(arr, options) { if (getType(arr) !== 'array') throw ('參數需為array') funcArr.forEach(key => {  arr[key] = function(...args) {   this.__proto__[key].call(this, ...args)   setCallBack(this[Symbol.for('parent')], this[Symbol.for('key')], options)  } })}function dep(obj, key, options) { let data = obj[key] if (typeArr.includes(getType(data))) {  data[Symbol.for('parent')] = obj  data[Symbol.for('key')] = key } Object.defineProperty(obj, key, {  configurable: true,  get() {   if (options && options.get) {    options.get(obj, key)   }   return data  },  set(val) {   if (val === data) return   data = val   let index = typeArr.indexOf(getType(data))   if (index >= 0) {    data[Symbol.for('parent')] = obj    data[Symbol.for('key')] = key    if (index) {     rewriteArrFunc(data, options)    } else {     observer(data, options)    }   }   setCallBack(obj, key, options)  } })}function observer(obj, options) { if (getType(obj) !== 'object') throw ('參數需為object') let index Object.keys(obj).forEach(key => {  dep(obj, key, options)  index = typeArr.indexOf(getType(obj[key]))  if (index < 0) return  if (index) {   rewriteArrFunc(obj[key], options)  } else {   observer(obj[key], options)  } })}function debounceStorage(state, fn, delay) { if(getType(fn) !== 'function') return null let updateItems = new Set() let timer = null return function setToStorage(obj, key) {  let changeKey = getStoragePath(obj, key)[0]  updateItems.add(changeKey)  clearTimeout(timer)  timer = setTimeout(() => {   try {    updateItems.forEach(key => {     fn.call(this, key, state[key])    })    updateItems.clear()   } catch (e) {    console.error(`persistent.js中state內容持久化失敗,錯誤位于[${changeKey}]參數中的[${key}]項`)   }  }, delay) }}export function getStoragePath(obj, key) { let storagePath = [key] while (obj) {  if (obj[Symbol.for('key')]) {   key = obj[Symbol.for('key')]   storagePath.unshift(key)  }  obj = obj[Symbol.for('parent')] } return storagePath}export function persistedState({state, setItem, getItem, setDelay=0, getDelay=0}) { observer(state, {  set: debounceStorage(state, setItem, setDelay),  get: debounceStorage(state, getItem, getDelay) })}/*vuex自動配置mutation相關方法*/export function setMutations(stateReplace, mutationsReplace) { Object.keys(stateReplace).forEach(key => {  let name = key.replace(//w/, (first) => `update${first.toUpperCase()}`)  let replaceState = (key, state, payload) => {   state[key] = payload  }  mutationsReplace[name] = (state, payload) => {   replaceState(key, state, payload)  } })}/*通用方法*/export function getType(para) { return Object.prototype.toString.call(para)  .replace(//[object (.+?)/]/, '$1').toLowerCase()}

persistent.js中調用:

import {persistedState} from '../common/tools.js'......// 因為是uni-app小程序,持久化是調用uni.setStorageSync,網頁就用localStorage.setItempersistedState({state, setItem: uni.setStorageSync, setDelay: 1000})

源碼地址

https://github.com/goblin-pitcher/uniapp-miniprogram

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 东乡族自治县| 石楼县| 克山县| 吉安市| 南投县| 东阿县| 雷州市| 赤峰市| 元谋县| 荆州市| 南澳县| 高平市| 凉城县| 海林市| 文水县| 镇平县| 中超| 清苑县| 四会市| 山阳县| 阿克| 邯郸市| 如皋市| 平陆县| 阿鲁科尔沁旗| 长兴县| 通州市| 威宁| 开鲁县| 宜良县| 进贤县| 黄浦区| 陇南市| 个旧市| 友谊县| 建宁县| 道真| 平定县| 宁德市| 扶余县| 嘉荫县|