<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

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

        來源:懂視網 責編:小采 時間:2020-11-27 21:50:37
        文檔

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

        如何實現一個簡易版的vuex持久化工具:背景 最近用uni-app開發小程序項目時,部分需要持久化的內容沒法像其他vuex中的state那樣調用,所以想著自己實現一下類似vuex-persistedstate插件的功能,貌似代碼量也不會很大 初步思路 首先想到的實現方式自然是vue的watcher模式。對需要持久化
        推薦度:
        導讀如何實現一個簡易版的vuex持久化工具:背景 最近用uni-app開發小程序項目時,部分需要持久化的內容沒法像其他vuex中的state那樣調用,所以想著自己實現一下類似vuex-persistedstate插件的功能,貌似代碼量也不會很大 初步思路 首先想到的實現方式自然是vue的watcher模式。對需要持久化

        背景

        最近用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.setItem
        persistedState({state, setItem: uni.setStorageSync, setDelay: 1000})
        

        源碼地址

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

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

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

        如何實現一個簡易版的vuex持久化工具:背景 最近用uni-app開發小程序項目時,部分需要持久化的內容沒法像其他vuex中的state那樣調用,所以想著自己實現一下類似vuex-persistedstate插件的功能,貌似代碼量也不會很大 初步思路 首先想到的實現方式自然是vue的watcher模式。對需要持久化
        推薦度:
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 国产黄在线观看免费观看不卡| 亚洲自偷自拍另类12p| 亚洲av永久无码精品天堂久久| 日本免费在线中文字幕| 亚洲免费在线视频| 99久在线国内在线播放免费观看| 久久精品亚洲综合专区| 久久免费看少妇高潮V片特黄| 久久国产亚洲高清观看| 在线v片免费观看视频| 亚洲一线产区二线产区区| 成年人在线免费看视频| 国产精品亚洲色婷婷99久久精品| 免费a级毛片大学生免费观看| 亚洲精品国产日韩无码AV永久免费网| 亚洲色无码一区二区三区| 久久精品乱子伦免费| 亚洲国产综合在线| 啦啦啦www免费视频| 一个人免费观看www视频 | 成年轻人网站色免费看| 亚洲精品又粗又大又爽A片| 亚洲第一黄色网址| 久久99青青精品免费观看| 激情综合亚洲色婷婷五月APP| 国产精品免费小视频| a级毛片毛片免费观看久潮| 亚洲短视频在线观看| 国产免费观看黄AV片| 国产福利免费视频| 亚洲日本在线观看网址| 国产小视频在线免费| a级毛片毛片免费观看久潮喷| 国产精品亚洲综合五月天| mm1313亚洲精品国产| 久久久久免费看成人影片| 亚洲AV无码专区国产乱码不卡| 国产亚洲3p无码一区二区| 成人毛片免费网站| 免费视频成人手机在线观看网址| 国产精品亚洲片夜色在线|