在開發 vue 的時候,我們可以使用 watch 和 computed 很方便的檢測數據的變化,從而做出相應的改變,但是在小程序里,只能在數據改變時手動觸發 this.setData() ,那么如何給小程序也加上這兩個功能呢?
我們知道在 vue 里是通過 Object.defineProperty 來實現數據變化檢測的,給該變量的 setter 里注入所有的綁定操作,就可以在該變量變化時帶動其它數據的變化。那么是不是可以把這種方法運用在小程序上呢?
實際上,在小程序里實現要比 vue 里簡單,應為對于 data 里對象來說,vue 要遞歸的綁定對象里的每一個變量,使之響應式化。但是在微信小程序里,不管是對于對象還是基本類型,只能通過 this.setData() 來改變,這樣我們只需檢測 data 里面的 key 值的變化,而不用檢測 key 值里面的 key 。
先上測試代碼
<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)}
新聞熱點
疑難解答