在小程序項(xiàng)目中, 我們的通常會使用到使用到一個全局對象作為各個頁面通用的數(shù)據(jù)存儲容器, 將它綁定到app對象后, 就能在每一個頁面都自由的操縱這個對象. 然而在實(shí)踐中, 由于這個對象及其屬性不具備響應(yīng)式條件, 它不能直接參與業(yè)務(wù)邏輯的編寫, 能力僅僅局限于數(shù)據(jù)儲存. 若是在VueJS項(xiàng)目中, 我們可能經(jīng)常使用到 Vue.$watch 去偵聽某個數(shù)據(jù)是否發(fā)生變化, 小程序卻缺乏這種能力.
在這篇文章中, 我將用150行代碼, 手把手帶你打造一個小程序也可以使用的偵聽器(下簡稱VX):
// 一個快速賦值的語法糖函數(shù), 可以創(chuàng)建結(jié)構(gòu)為 { value: a { b: { val: ''} } } 的對象vx.set('value.a.d', { val: '' })// 對某個屬性進(jìn)行偵聽, 如果發(fā)生改變, 則執(zhí)行相應(yīng)函數(shù)(可多次watch以執(zhí)行多個函數(shù))vx.watch('value.a.d.val', newVal => { console.log(`val改變?yōu)?: `, newVal)})value.a.d.val = 3 // val改編為 : 3使用VX偵聽器, 我們可以更加方便的管理各個頁面的狀態(tài). 同時, 我們憑借 watch 語法, 可以更優(yōu)雅地編寫業(yè)務(wù)邏輯.
坐穩(wěn)了, 三輪車準(zhǔn)備啟動了~ 各位評論見~ :yum:
稍微理一理思路
在全局對象中, 我們不一定要對每一個屬性都進(jìn)行偵聽, 所以VX主要的功能就是通過set去設(shè)置某個具體屬性的setter/getter, 同時通過watch向添加該屬性添加需要訂閱的回調(diào)函數(shù).
依賴對象的實(shí)現(xiàn)
首先我們需要造一個通用的 依賴對象 , 依賴對象攜帶一個訂閱數(shù)組用于存放一組回調(diào)函數(shù), 同時它還應(yīng)該包括一些操作訂閱數(shù)組能力(如添加訂閱, 清空訂閱)的函數(shù)
class Dep { constructor () { this.subs = [] } // 將回調(diào)添加到數(shù)組中 addSub (fn) { /*...*/ } delSub (fn) { /*...*/ } // 執(zhí)行數(shù)組中每一項(xiàng)函數(shù) notify (newVal, oldVal) { this.subs.forEach(func => func(newVal, oldVal)) }}全局對象中每一個響應(yīng)式屬性(及其每一個子屬性), 都應(yīng)該和一個新的Dep實(shí)例保持一一對應(yīng)的關(guān)系, 這樣我們進(jìn)行偵聽變化, 執(zhí)行訂閱的回調(diào)函數(shù)時, 只需要找到對應(yīng)的實(shí)例執(zhí)行 notify 通知更新即可.
設(shè)置響應(yīng)式屬性
defineProperty
可能是因?yàn)榻佑|DefineProperty要比接觸Proxy早一些的緣故, 代碼使用了前者進(jìn)行響應(yīng)式的實(shí)現(xiàn), Object.defineProperty方法會直接在一個對象上定義一個新屬性, 這里快速過一遍 defineProperty 具體配置:
// @param obj 要在其上定義屬性的對象// @param key 要定義或修改的屬性的名稱Object.defineProperty(obj, key, { // 該屬性是否能被枚舉 enumerable: true, // 該屬性能否被刪除 configurable: true, // 訪問該屬性則會執(zhí)行此方法 get: () => { return val }, // 修改該屬性時會執(zhí)行此方法 set: newVal => { val = newVal }, // value & writeble 不能和 getter/setter 同時出現(xiàn)})
新聞熱點(diǎn)
疑難解答