亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

150行代碼帶你實現微信小程序中的數據偵聽

2019-11-19 11:32:35
字體:
來源:轉載
供稿:網友

在小程序項目中, 我們的通常會使用到使用到一個全局對象作為各個頁面通用的數據存儲容器, 將它綁定到app對象后, 就能在每一個頁面都自由的操縱這個對象. 然而在實踐中, 由于這個對象及其屬性不具備響應式條件, 它不能直接參與業務邏輯的編寫, 能力僅僅局限于數據儲存. 若是在VueJS項目中, 我們可能經常使用到 Vue.$watch 去偵聽某個數據是否發生變化, 小程序卻缺乏這種能力.

在這篇文章中, 我將用150行代碼, 手把手帶你打造一個小程序也可以使用的偵聽器(下簡稱VX):

// 一個快速賦值的語法糖函數, 可以創建結構為 { value: a { b: { val: ''} } } 的對象vx.set('value.a.d', { val: '' })// 對某個屬性進行偵聽, 如果發生改變, 則執行相應函數(可多次watch以執行多個函數)vx.watch('value.a.d.val', newVal => { console.log(`val改變為 : `, newVal)})value.a.d.val = 3 // val改編為 : 3

使用VX偵聽器, 我們可以更加方便的管理各個頁面的狀態. 同時, 我們憑借 watch 語法, 可以更優雅地編寫業務邏輯.

坐穩了, 三輪車準備啟動了~ 各位評論見~ :yum:

稍微理一理思路

在全局對象中, 我們不一定要對每一個屬性都進行偵聽, 所以VX主要的功能就是通過set去設置某個具體屬性的setter/getter, 同時通過watch向添加該屬性添加需要訂閱的回調函數.

依賴對象的實現

首先我們需要造一個通用的 依賴對象 , 依賴對象攜帶一個訂閱數組用于存放一組回調函數, 同時它還應該包括一些操作訂閱數組能力(如添加訂閱, 清空訂閱)的函數

class Dep { constructor () { this.subs = [] } // 將回調添加到數組中 addSub (fn) { /*...*/ } delSub (fn) { /*...*/ } // 執行數組中每一項函數 notify (newVal, oldVal) { this.subs.forEach(func => func(newVal, oldVal)) }}

全局對象中每一個響應式屬性(及其每一個子屬性), 都應該和一個新的Dep實例保持一一對應的關系, 這樣我們進行偵聽變化, 執行訂閱的回調函數時, 只需要找到對應的實例執行 notify 通知更新即可.

設置響應式屬性

defineProperty

可能是因為接觸DefineProperty要比接觸Proxy早一些的緣故, 代碼使用了前者進行響應式的實現, Object.defineProperty方法會直接在一個對象上定義一個新屬性, 這里快速過一遍 defineProperty 具體配置:

// @param obj 要在其上定義屬性的對象// @param key 要定義或修改的屬性的名稱Object.defineProperty(obj, key, { // 該屬性是否能被枚舉 enumerable: true, // 該屬性能否被刪除 configurable: true, // 訪問該屬性則會執行此方法 get: () => { return val }, // 修改該屬性時會執行此方法 set: newVal => { val = newVal }, // value & writeble 不能和 getter/setter 同時出現})

通過對defineProperty進行上層封裝, 我們可以輕松的實現在全局對象上設置響應式屬性功能, 在此, 我們結合剛才定義的Dep對象, 將一個新的dep實例綁定到新增屬性的setter中:

set (key, val, options = {}, obj = this.store) { const dep = new Dep() Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: () => {  return val }, set: newVal => {  if (newVal === val) {  return  }  dep.notify(newVal, val)  val = newVal } })}

每當對應屬性被賦值, 就會執行依賴數組中的回調函數.

不過這樣還不夠, 我們還得想辦法獲取到這個dep實例, 才能給它的依賴數組填充函數.

這邊提供一個很簡單的思路, 并不推薦實踐中這么做:

set (key, val, options = {}, obj = this.store) { const dep = new Dep() Object.defineProperty(obj, key, {})+ return dep}const valueDep = set('value', b, {})valueDep.addSub(() => { console.log('value changed!') })

雖然代碼能使用了, 就是是看起來怪怪的~ :yum: 我們的三輪車開進了岔路~

通過watch添加訂閱

喝口水我們繼續

<黑客與畫家>一書中曾經提到這樣一個觀點, 我深有體會:

如果你覺得你的代碼奇怪, 那么往往它是錯的

上面的那一串代碼僅僅是能跑通的水平, 我們需要加入更多的細節和思考, 有時候只需要坐下來稍微看一下代碼, 就會有各種想法蹦出來:

構思這種東西有一個特點,那就是它會導致更多的構思。你有沒有注意過,坐下來寫東西的時候,一半的構思是寫作時產生的?

隱藏Dep

這些內容應和外部是解耦的. 首先一點, 我們創建一個偵聽器類, 用于封裝我們偵聽所用到的所有方法, 它包含了我們想要的全局對象以及操作它的方法(如watch,set):

class VX { constructor () { this.store = Object.create(null) } watch (key, fn, obj = this.store) {} set (key, val, options = {}, obj = this.store) {}}const vx = new VX()

我們可以在watch中給對象某個屬性添加回調, 就不用去直接操作Dep依賴數組了. 只是, 我們在業務代碼中調用watch, 要怎么去獲取obj.key對應的dep呢?

我們設置一個全局的depHandler, 在obj.key的getter中主動將depHandler設置為當前obj.key的dep實例, 那么我們在watch函數里, 只要用任意操作觸發obj.key的getter, 就能通過depHandler得到它的dep實例了, 代碼形如:

+ // 一開始沒有持有dep實例+ let handleDep = null class VX { watch (key, fn, obj = this.store) {+  console.log(obj.key) // 使用任意操作觸發obj.key的getter, 那么handleDep將自動引用obj.key的dep實例+  handleDep.addSub(fn) } set (key, val, options = {}, obj = this.store) {  const dep = new Dep()  Object.defineProperty(obj, key, {  enumerable: true,  configurable: true,  get: () => {+   handleDep = dep   return val  },  set: newVal => {}  }) } }

主動收集依賴

我們增加 handleDep.addSub(fn) 添加回調函數的邏輯, 其實可以直接放到getter中, 首先在Dep類中封裝一個'主動'收集依賴的 collect 方法, 他會將全局 handleFn 存放到訂閱數組中, 這樣一來, 在watch函數中, 我們只要觸發obj.key的getter, 就可以主動收集依賴了:

let handleFn = nullclass Dep { addSub (fn) {} delSub (fn) {} clear () {} collect (fn = handleFn) { if (fn && !this.subs.find(x => x === fn)) {  this.addSub(fn) } } notify (newVal, oldVal) {}}let handleDep = nullclass VX { watch (key, fn, obj = this.store) { handleFn = fn console.log(obj.key) } set (key, val, options = {}, obj = this.store) { const dep = new Dep() Object.defineProperty(obj, key, {  enumerable: true,  configurable: true,  get: () => {  handleDep = dep  handleDep.collect()  return val  },  set: newVal => {} }) }}

處理key值為對象鏈的情況

在先前的watch函數中, 我們使用console.log(obj.key)去觸發對應屬性的getter, 如果我們調用方式是 watch('a.b.c') 就無能為力了. 這里我們封裝一個通用方法, 用于處理對象鏈字符串的形式:

// 通過將字符串'a.b'分割為['a', 'b'], 再使用一個while循環就可以走完這個對象鏈function walkChains (key, obj) { const segments = key.split('.') let deepObj = obj while (segments.length) { deepObj = deepObj[segments.shift()] }}class VX { watch (key, fn, obj = this.store) { handleFn = fn walkChains(key, obj) }}

在set方法中處理對象鏈字符串稍微有些不同, 因為如果 set('a.b') 時, 沒有在我們全局對象中找到a屬性, 這里應該拋錯.

實際的處理中, 需要推斷'obj.a'以及'obj.a.b'是否存在. 假設沒有'obj.a', 那么我們應該創建一個新的對象, 并且給新的對象添加屬性'b', 所以代碼類似 walkChains 函數, 只是稍作一層判斷:

set (key, val, obj) { const segments = key.split('.') // 這里需要注意, 我們只處理到倒數第二個屬性 while (segments.length > 1) { const handleKey = segments.shift() const handleVal = obj[handleKey] // 存在'obj.a'的情況 if (typeof handleVal === 'object') {  obj = handleVal // 不存在'obj.a'則給a屬性賦一個非響應式的對象 } else if (!handleVal) {  obj = (  key = handleKey,  obj[handleKey] = {},  obj[handleKey]  ) } else {  console.trace('already has val') } } // 最后一個屬性要手動賦值 key = segments[0]}

業務場景應用

小程序跨頁面刷新數據

我們經常碰到在小程序中由A頁面跳轉到B頁面, 如果B頁面進行了一些操作, 希望A頁面自動刷新數據的情況. 但是由于A頁面跳轉到B頁面不同(也許是redirect,也許是navigate), 處理方法也不盡相同.

使用navigate方式跳轉后, A頁面不會被注銷, 所以我們一般會通過全局對象去貯存A頁面實例(也就是A頁面的this對象), 然后在B頁面直接調用相應的方法(如A.refreshList())進行刷新操作.

引入VX后, 我們可以在 onload 生命周期直接調用watch方法添加訂閱:

// app.jsimport VX from '@/utils/suites/vx'const vx = new VX()app.vx = vxapp.store = vx.storeapp.vx.set('userType', '商戶')// page aonLoad () { app.vx.watch('userType', userType => { if (userType === '商戶') {  // ... } else if (userType === '管理員') {  // ... } }, { immediate: true })}// page bswitchUserType () { app.store.userType = '管理員'}

可能遇到的問題

給watch方法添加的函數設置立即執行

有的時候我們希望通過watch添加函數的同時還立即執行該函數一次, 這個時候我們需要再定義額外的參數傳遞到watch中. 問題是這個函數不一定是同步函數.

簡單處理如下:

class VX { async watch (key, fn, options = { immediately: false }, obj = this.store) { handleDep = fn walkChains(key, obj) options.immediately && await fn(options.defaultParams) }}

this綁定丟失問題

在我在對VX進行刪除屬性方法的擴展時, 我往walkChain函數中添加了一個執行回調函數的機制, 并且在刪除屬性這個方法直接調用了walkChain:

+ function walkChains (key, obj, fn) { const segments = key.split('.') let deepObj = obj while (segments.length) {  deepObj = deepObj[segments.shift()]+  fn && fn() } }del (key, obj = this.store) { walkChains(key, obj, handleDep.clear) delete obj[key]}

因為handleDep.clear當成參數傳遞進walkChains中會 丟失this綁定 , 所以上面那段代碼其實是有問題的, 不過稍作修改就好了:

del (key, obj = this.store) {+ walkChains(key, obj, () => handleDep.clear()) delete obj[key] }

總結

以上所述是小編給大家介紹的150行代碼帶你實現微信小程序中的數據偵聽,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
大荫蒂欧美视频另类xxxx| 亚洲国产精品久久91精品| 日韩精品丝袜在线| 成人亚洲欧美一区二区三区| 亚洲日本欧美日韩高观看| 欧美高清在线播放| 欧美丰满老妇厨房牲生活| 国产精品久久久久99| 在线a欧美视频| 国产精品久久电影观看| 91av国产在线| 亚洲第一网站男人都懂| 日韩电影中文字幕在线观看| 国产精品一区二区女厕厕| 美女av一区二区三区| 欧美国产日韩一区二区三区| 91精品久久久久久久久久久久久| xvideos亚洲人网站| 日本高清不卡在线| 国产精品久久激情| 色婷婷综合久久久久| 日韩av影片在线观看| 欧美在线观看一区二区三区| 欧美精品久久久久久久| 性欧美在线看片a免费观看| 亚洲色图日韩av| 日韩成人中文电影| 欧洲亚洲女同hd| 日韩免费在线视频| 国产精品一区二区久久久久| 国产日韩欧美中文| 91青草视频久久| 精品一区二区三区四区在线| 欧美日韩国产激情| 日韩中文字幕免费视频| 国产欧美一区二区三区在线看| 在线亚洲男人天堂| 国色天香2019中文字幕在线观看| 国产精品∨欧美精品v日韩精品| 日本成人黄色片| 日韩电影中文字幕在线观看| 自拍亚洲一区欧美另类| 国产中文字幕亚洲| 久久久999精品| 欧美极品在线视频| 亚洲第一色中文字幕| 国产精品入口福利| 亚洲午夜色婷婷在线| 国产不卡在线观看| 久久精品最新地址| 7777kkkk成人观看| 中文字幕亚洲自拍| 九九精品视频在线观看| 日韩女优人人人人射在线视频| 亚洲成在人线av| www.久久色.com| 亚洲欧美中文日韩在线v日本| 欧美激情按摩在线| 亚洲第一区中文99精品| 国产成人精品免费视频| 亚洲丁香久久久| 日韩av网站在线| 91精品综合久久久久久五月天| 国产精品夜色7777狼人| 欧美高清在线观看| 欧美午夜精品久久久久久人妖| 国产成人精品视频在线观看| 国产精品久久久久久久久免费| 亚洲欧美视频在线| 国产欧美精品在线播放| 清纯唯美日韩制服另类| 26uuu国产精品视频| 久久久精品久久久久| 欧美电影在线观看网站| 中文字幕亚洲欧美日韩高清| 狠狠久久亚洲欧美专区| 久久久久久成人精品| 国产精品美女久久久久久免费| 久久精品国产一区二区三区| 中文字幕欧美精品日韩中文字幕| 中文字幕视频在线免费欧美日韩综合在线看| 久久久噜噜噜久噜久久| 97久久精品在线| 国产亚洲成av人片在线观看桃| 欧美亚州一区二区三区| 国产欧美日韩中文字幕在线| 性日韩欧美在线视频| 96精品久久久久中文字幕| 亚洲最大福利网站| 8x拔播拔播x8国产精品| 欧美色图在线视频| 自拍视频国产精品| 亚洲人成77777在线观看网| 最近2019年日本中文免费字幕| 日韩美女福利视频| 成人免费视频网址| 国产亚洲精品日韩| 日韩小视频在线观看| 狠狠躁18三区二区一区| 欧美极品第一页| 国产精品一区二区三区免费视频| 国产精品一区二区3区| 97精品久久久中文字幕免费| 国产成人久久久| 狠狠做深爱婷婷久久综合一区| 欧美激情精品久久久久久黑人| 人人爽久久涩噜噜噜网站| 亚洲国产精品久久91精品| 久久久精品亚洲| 97久久精品人搡人人玩| 国产精品成人品| 亚洲一区二区福利| 欧美人交a欧美精品| 欧洲亚洲免费在线| 91在线中文字幕| 成人国产亚洲精品a区天堂华泰| 久久99热这里只有精品国产| 亚洲精品自产拍| 国产91ⅴ在线精品免费观看| 亚洲午夜久久久影院| 国产视频丨精品|在线观看| 国产成人一区二区三区小说| 九九九久久国产免费| 日日骚久久av| 91在线色戒在线| 国产久一一精品| 在线观看国产精品日韩av| 欧美丰满少妇xxxxx| 日韩视频中文字幕| 亚洲丝袜在线视频| 国产亚洲欧洲高清| 成人网欧美在线视频| 激情亚洲一区二区三区四区| 亚洲男女性事视频| 国产在线播放91| 国产精品白丝jk喷水视频一区| 九九热视频这里只有精品| 在线看国产精品| 国产精品直播网红| 91日韩在线播放| 久久亚洲精品成人| 国产做受高潮69| 久久久免费精品| 日韩中文字幕在线| 日韩视频免费大全中文字幕| 亚洲一级一级97网| 国产精品福利片| 欧美日韩国产中文精品字幕自在自线| 欧美风情在线观看| 亚洲成人av中文字幕| 福利一区福利二区微拍刺激| 欧美激情综合色综合啪啪五月| 久久精品国产一区二区电影| 精品国产一区二区在线| 亚洲欧美中文日韩v在线观看| 欧美一级视频一区二区| 国产成人精品久久二区二区91| 国产日韩精品入口| 91九色视频导航| 亚洲精品女av网站| 91sao在线观看国产| 国产精自产拍久久久久久蜜| 久久精品一偷一偷国产| 日韩在线播放一区|