在vue中,computed是一個計算屬性,類似於過濾器,對綁定到view的資料進行處理,並監聽變化。而watch監聽複雜資料類型需用深度監聽。這兩者都可以在vue上實現檢測資料的變化。而微信小程式不同於vue可以使用watch和computed做出對應的改變。小程式中只有函數this.setData()可以偵測數據,所以小程式每次資料改變需要偵測時都必須手動執行函數才可實現。除此之外,小程式還可以附上這兩個功能來偵測資料變化。
vue 裡是透過 Object.defineProperty 來實現資料變化偵測的,給該變數的 setter 裡注入所有的綁定操作,就可以在變數變更時帶動其它資料的變化。實際上,在小程式裡實作要比 vue 裡簡單,應為對於 data 裡物件來說,vue 要遞歸的綁定物件裡的每一個變量,使其響應式化。但在微信小程式裡,不管是對物件或基本型,只能透過 this.setData() 來改變,這樣我們只要偵測 data 裡面的 key 值的變化,而無法偵測 key 值裡面的 key 。
測試程式碼:
Page({ data: { test: { a: 123 }, test1: \'test1\', }, onLoad() { computed(this, { test2: function() { returnthis.data.test.a + \'2222222\' }, test3: function() { returnthis.data.test.a + \'3333333\' } }) watch(this, { test:function(newVal) { console.log(\'invoke watch\') this.setData({test1: newVal.a + \'11111111\' }) } }) }, changeTest() { this.setData({ test:{ a: Math.random().toFixed(5) } }) }, })
現在我們要實作 watch 和 computed 方法,使得 test 變化時,test1、test2、test3 也變化,為此,增加了一個按鈕,點擊這個按鈕時,test 會改變。
watch 方法相對簡單點,首先我們定義一個函數來偵測變化:
function defineReactive(data, key, val, fn) { Object.defineProperty(data, key, { configurable: true, enumerable: true, get: function() { return val }, set: function(newVal){ if (newVal === val)return fn &&fn(newVal) val = newVal }, }) }
然後遍歷 watch 函數傳入的對象,給每個鍵呼叫該方法
function watch(ctx, obj) { Object.keys(obj).forEach(key => { defineReactive(ctx.data, key, ctx.data[key], function(value) { obj[key].call(ctx,value) }) }) }
這裡有參數是 fn ,也就是上面 watch 方法裡 test 的值,這裡把方法包一層,綁定 context。
接著來看 computed,這個稍微複雜,因為我們無法得知 computed 裡依賴的是 data 裡面的哪個變量,因此只能遍歷 data 裡的每個變數。
function computed(ctx, obj) { let keys =Object.keys(obj) let dataKeys =Object.keys(ctx.data) dataKeys.forEach(dataKey => { defineReactive(ctx.data, dataKey, ctx.data[dataKey]) }) let firstComputedObj =keys.reduce((prev, next) => { ctx.data.$target =function() { ctx.setData({[next]: obj[next].call(ctx) }) } prev[next] =obj[next].call(ctx) ctx.data.$target =null return prev }, {}) ctx.setData(firstComputedObj) }
詳細解釋下這段程式碼,首先給 data 裡的每個屬性呼叫 defineReactive 方法。接著計算 computed 裡面每個屬性第一次的值,也就是上例的 test2、test3。
computed(this, { test2: function() { returnthis.data.test.a + \'2222222\' }, test3: function() { returnthis.data.test.a + \'3333333\' } })
這裡分別呼叫 test2 和 test3 的值,將回傳值與對應的 key 值組合成一個對象,然後再呼叫 setData() ,這樣就會第一次計算這兩個值,這裡使用了 reduce 方法。 test2 和 test3 都是依賴 test 的,因此必須在 test 改變的時候在其的 setter 函數呼叫 test2 和 test3 中對應的函數,並且透過 setData 來設定這兩個變數。
小型程式商店提供更多上線小程式
宣告了一個變數來保存所有在變更時需要執行的函數,在 set 時執行每一個函數,因為此時 this.data.test 的數值尚未改變,使用 setTimeout 在下一輪再執行。現在就有一個問題,怎麼將函數加入 subs 。不知道各位還是否記得上面我們說到的在 reduce 裡的兩行程式碼。因為在執行計算 test1 和 test2 第一次 computed 值的時候,會呼叫 test 的 getter 方法,而此刻就是一個好機會將函數注入到 subs 中,在 data 上宣告一個 $target 變數,並將需要執行的函數賦值給這個變量,這樣在 getter 中就可以判斷 data 上有無 target 值,從而可以 push 進 subs,注意的是需要馬上將 target 設為 null,
#到此已經實現了 target 設為 null,
#到此為止已經實現了 target 和 computed,但還沒完,有個問題。當同時使用這兩者的時候,watch 裡的物件的鍵也同時存在於 data 中,這樣就會重複在該變數上呼叫 Object.defineProperty ,後面會覆蓋前面。因為這裡不像 vue 裡可以決定兩者的呼叫順序,因此我們推薦先寫 computed 再寫 watch,這樣可以 watch computed 裡的數值。 推薦:《
小程式開發教學###》###以上是小程式如何透過watch和computed檢測數據的詳細內容。更多資訊請關注PHP中文網其他相關文章!