您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關watch和computed怎么在微信小程序中使用,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
先上測試代碼
<view>{{ test.a }}</view> <view>{{ test1 }}</view> <view>{{ test2 }}</view> <view>{{ test3 }}</view> <button bindtap="changeTest">change</button>
const { watch, computed } = require('./vuefy.js') Page({ data: { test: { a: 123 }, test1: 'test1', }, onLoad() { computed(this, { test2: function() { return this.data.test.a + '2222222' }, test3: function() { return this.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() { return this.data.test.a + '2222222' }, test3: function() { return this.data.test.a + '3333333' } })
這里分別調用 test2 和 test3 的值,將返回值與對應的 key 值組合成一個對象,然后再調用 setData()
,這樣就會第一次計算這兩個值,這里使用了 reduce
方法。但是你可能會發現其中這兩行代碼,它們好像都沒有被提到是干嘛用的。
ctx.data.$target = function() { ctx.setData({ [next]: obj[next].call(ctx) }) } ctx.data.$target = null
可以看到,test2 和 test3 都是依賴 test 的,這樣必須在 test 改變的時候在其的 setter 函數中調用 test2 和 test3 中對應的函數,并通過 setData 來設置這兩個變量。為此,需要將 defineReactive 改動一下。
function defineReactive(data, key, val, fn) { let subs = [] // 新增 Object.defineProperty(data, key, { configurable: true, enumerable: true, get: function() { // 新增 if (data.$target) { subs.push(data.$target) } return val }, set: function(newVal) { if (newVal === val) return fn && fn(newVal) // 新增 if (subs.length) { // 用 setTimeout 因為此時 this.data 還沒更新 setTimeout(() => { subs.forEach(sub => sub()) }, 0) } val = newVal }, }) }
相較于之前,增加了幾行代碼,我們聲明了一個變量來保存所有在變化時需要執行的函數,在 set 時執行每一個函數,因為此時 this.data.test
的值還未改變,使用 setTimeout 在下一輪再執行。現在就有一個問題,怎么將函數添加到 subs 中。不知道各位還是否記得上面我們說到的在 reduce 里的那兩行代碼。因為在執行計算 test1 和 test2 第一次 computed 值的時候,會調用 test 的 getter 方法,此刻就是一個好機會將函數注入到 subs 中,在 data 上聲明一個 $target 變量,并將需要執行的函數賦值給該變量,這樣在 getter 中就可以判斷 data 上有無 target 值,從而就可以 push 進 subs,要注意的是需要馬上將 target 設為 null,這就是第二句的用途,這樣就達到了一石二鳥的作用。當然,這其實就是 vue 里的原理,只不過這里沒那么復雜。
到此為止已經實現了 watch 和 computed,但是還沒完,有個問題。當同時使用這兩者的時候,watch 里的對象的鍵也同時存在于 data 中,這樣就會重復在該變量上調用 Object.defineProperty
,后面會覆蓋前面。因為這里不像 vue 里可以決定兩者的調用順序,因此我們推薦先寫 computed 再寫 watch,這樣可以 watch computed 里的值。這樣就有一個問題,computed 會因覆蓋而無效。
思考一下為什么?
很明顯,這時因為之前的 subs 被重新聲明為空數組了。這時,我們想一個簡單的方法就是把之前 computed 里的 subs 存在一個地方,下一次調用 defineReactive
的時候看對應的 key 是否已經有了 subs,這樣就可以解決問題。修改一下代碼。
function defineReactive(data, key, val, fn) { let subs = data['$' + key] || [] // 新增 Object.defineProperty(data, key, { configurable: true, enumerable: true, get: function() { if (data.$target) { subs.push(data.$target) data['$' + key] = subs // 新增 } return val }, set: function(newVal) { if (newVal === val) return fn && fn(newVal) if (subs.length) { // 用 setTimeout 因為此時 this.data 還沒更新 setTimeout(() => { subs.forEach(sub => sub()) }, 0) } val = newVal }, }) }
以上就是watch和computed怎么在微信小程序中使用,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。