最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何實(shí)現(xiàn)一個(gè)簡(jiǎn)易版的vuex持久化工具

 更新時(shí)間:2019年09月11日 09:38:02   作者:goblin_pitcher  
這篇文章主要介紹了實(shí)現(xiàn)一個(gè)簡(jiǎn)易版的vuex持久化工具,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

背景

最近用uni-app開發(fā)小程序項(xiàng)目時(shí),部分需要持久化的內(nèi)容沒(méi)法像其他vuex中的state那樣調(diào)用,所以想著自己實(shí)現(xiàn)一下類似vuex-persistedstate插件的功能,貌似代碼量也不會(huì)很大

初步思路

首先想到的實(shí)現(xiàn)方式自然是vue的watcher模式。對(duì)需要持久化的內(nèi)容進(jìn)行劫持,當(dāng)內(nèi)容改變時(shí),執(zhí)行持久化的方法。
先弄個(gè)dep和observer,直接observer需要持久化的state,并傳入get和set時(shí)的回調(diào):

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 ('參數(shù)需為object')
 Object.keys(obj).forEach(key => {
  dep(obj, key, options)
  if(getType(obj[key]) === 'object') {
   observer(obj[key], options)
  }
 })
}

然而很快就發(fā)現(xiàn)問(wèn)題,若將a={b:{c:d:{e:1}}}存入storage,操作一般是xxstorage('a',a),接下來(lái)無(wú)論是改了a.b還是a.b.c或是a.b.c.d.e,都需要重新執(zhí)行xxstorage('a',a),也就是無(wú)論a的哪個(gè)后代節(jié)點(diǎn)變動(dòng)了,重新持久化的都是整個(gè)object樹,所以監(jiān)測(cè)到某個(gè)根節(jié)點(diǎn)的后代節(jié)點(diǎn)變更后,需要先找到根節(jié)點(diǎn),再將根節(jié)點(diǎn)對(duì)應(yīng)的項(xiàng)重新持久化。

接下來(lái)的第一個(gè)問(wèn)題就是,如何找到變動(dòng)節(jié)點(diǎn)的父節(jié)點(diǎn)。

state樹的重新構(gòu)造

如果沿著state向下找到變動(dòng)的節(jié)點(diǎn),并根據(jù)找到節(jié)點(diǎn)的路徑確認(rèn)變動(dòng)項(xiàng),復(fù)雜度太高。

如果在observer的時(shí)候,對(duì)state中的每一項(xiàng)增添一個(gè)指向父節(jié)點(diǎn)的指針,在后代節(jié)點(diǎn)變動(dòng)時(shí),是不是就能沿著指向父節(jié)點(diǎn)的指針找到相應(yīng)的根節(jié)點(diǎn)了?

為避免新增的指針被遍歷到,決定采用Symbol,于是dep部分變動(dòng)如下:

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)
   }
   ...
  }
 })
}

再加個(gè)可以找到根節(jié)點(diǎn)的方法,就可以改變對(duì)應(yīng)storage項(xiàng)了

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]就是根節(jié)點(diǎn),storagePath記錄了從根節(jié)點(diǎn)到變動(dòng)節(jié)點(diǎn)的路徑
 return storagePath 
}

但是問(wèn)題又來(lái)了,object是可以實(shí)現(xiàn)自動(dòng)持久化了,數(shù)組用push、pop這些方法操作時(shí),數(shù)組的地址是沒(méi)有變動(dòng)的,defineProperty根本監(jiān)測(cè)不到這種地址沒(méi)變的情況(可惜Proxy兼容性太差,小程序中安卓直接不支持)。當(dāng)然,每次操作數(shù)組時(shí),對(duì)數(shù)組重新賦值可以解決此問(wèn)題,但是用起來(lái)太不方便了。

改變數(shù)組時(shí)的雙向綁定

數(shù)組的問(wèn)題,解決方式一樣是參照vue源碼的處理,重寫數(shù)組的'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'方法
數(shù)組用這7種方法操作數(shù)組的時(shí)候,手動(dòng)觸發(fā)set中部分,更新storage內(nèi)容

添加防抖

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

實(shí)現(xiàn)代碼

最后代碼如下:

tool.js:

/*
持久化相關(guān)內(nèi)容
*/
// 重寫的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 ('參數(shù)需為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 ('參數(shù)需為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內(nèi)容持久化失敗,錯(cuò)誤位于[${changeKey}]參數(shù)中的[${key}]項(xiàng)`)
   }
  }, 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自動(dòng)配置mutation相關(guān)方法
*/
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中調(diào)用:

import {persistedState} from '../common/tools.js'
...
...
// 因?yàn)槭莡ni-app小程序,持久化是調(diào)用uni.setStorageSync,網(wǎng)頁(yè)就用localStorage.setItem
persistedState({state, setItem: uni.setStorageSync, setDelay: 1000})

源碼地址

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Vue實(shí)現(xiàn)版本檢測(cè)與升級(jí)提示

    Vue實(shí)現(xiàn)版本檢測(cè)與升級(jí)提示

    在現(xiàn)代Web應(yīng)用開發(fā)中,版本檢測(cè)與升級(jí)提示是提升用戶體驗(yàn)的重要環(huán)節(jié),本文將詳細(xì)介紹如何在Vue應(yīng)用中實(shí)現(xiàn)這一功能,有需要的小伙伴可以參考一下
    2025-04-04
  • Vue 調(diào)試訪問(wèn)本地后端接口配置

    Vue 調(diào)試訪問(wèn)本地后端接口配置

    記錄一下本地測(cè)試前端的時(shí)候怎么訪問(wèn)本地后端接口,文中給大家提到了vue如何做調(diào)試后臺(tái)接口的配置和proxy的工作原理以及為什么能解決跨域,感興趣的朋友跟隨小編一起看看吧
    2023-06-06
  • vue3簡(jiǎn)單封裝input組件和統(tǒng)一表單數(shù)據(jù)詳解

    vue3簡(jiǎn)單封裝input組件和統(tǒng)一表單數(shù)據(jù)詳解

    最近有一個(gè)需求是很多個(gè)表單添加,編輯等操作,會(huì)用到很多input輸入框,所以就想把input進(jìn)行簡(jiǎn)單封裝,這篇文章主要給大家介紹了關(guān)于vue3簡(jiǎn)單封裝input組件和統(tǒng)一表單數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • Vue封裝遠(yuǎn)程下拉框組件的實(shí)現(xiàn)示例

    Vue封裝遠(yuǎn)程下拉框組件的實(shí)現(xiàn)示例

    本文主要介紹了Vue封裝遠(yuǎn)程下拉框組件的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • vant-list組件觸發(fā)多次onload事件導(dǎo)致數(shù)據(jù)亂序的解決方案

    vant-list組件觸發(fā)多次onload事件導(dǎo)致數(shù)據(jù)亂序的解決方案

    這篇文章主要介紹了vant-list組件觸發(fā)多次onload事件導(dǎo)致數(shù)據(jù)亂序的解決方案
    2023-01-01
  • 在vue項(xiàng)目中promise解決回調(diào)地獄和并發(fā)請(qǐng)求的問(wèn)題

    在vue項(xiàng)目中promise解決回調(diào)地獄和并發(fā)請(qǐng)求的問(wèn)題

    這篇文章主要介紹了在vue項(xiàng)目中promise解決回調(diào)地獄和并發(fā)請(qǐng)求的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • vscode jsconfig.json 使用簡(jiǎn)介

    vscode jsconfig.json 使用簡(jiǎn)介

    通過(guò)jsconfig.json文件定義一個(gè)JavaScript項(xiàng)目,目錄中是否存在此類文件表示該目錄是JavaScript項(xiàng)目的根目錄,文件本身可以選擇列出屬于項(xiàng)目的文件,要從項(xiàng)目中排除的文件以及編譯器選項(xiàng),這篇文章主要介紹了vscode jsconfig.json 使用說(shuō)明,需要的朋友可以參考下
    2023-09-09
  • vue組件通信的多種方法詳解

    vue組件通信的多種方法詳解

    這篇文章主要為大家介紹了vue組件通信的幾種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-11-11
  • vue3 :deep()的使用教程

    vue3 :deep()的使用教程

    對(duì)應(yīng)前端小白來(lái)說(shuō),一看到:deep()這些模式的用法就不理解是啥意思,下面簡(jiǎn)單介紹一下:deep()是什么以及如何使用,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • vue3?process.env.XXX環(huán)境變量不生效的解決方法

    vue3?process.env.XXX環(huán)境變量不生效的解決方法

    這篇文章主要給大家介紹了關(guān)于vue3?process.env.XXX環(huán)境變量不生效的解決方法,通過(guò)文中介紹的方法可以很方便的解決遇到的問(wèn)題,對(duì)大家學(xué)習(xí)或者使用vue3具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08

最新評(píng)論

承德市| 齐齐哈尔市| 新沂市| 普定县| 万州区| 长兴县| 綦江县| 仲巴县| 博罗县| 义马市| 福鼎市| 连云港市| 纳雍县| 凉城县| 临澧县| 隆子县| 和顺县| 新干县| 葵青区| 苗栗市| 呼和浩特市| 泾阳县| 全椒县| 陆河县| 凌海市| 双鸭山市| 夏津县| 绵竹市| 吉隆县| 永吉县| 额济纳旗| 从江县| 罗定市| 桦南县| 左云县| 紫云| 海晏县| 江油市| 巴马| 类乌齐县| 五台县|