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

首頁 > 課堂 > 小程序 > 正文

在微信小程序里使用watch和computed的方法

2020-03-21 16:12:45
字體:
來源:轉載
供稿:網友

在開發 vue 的時候,我們可以使用 watchcomputed 很方便的檢測數據的變化,從而做出相應的改變,但是在小程序里,只能在數據改變時手動觸發 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)}

詳細解釋下這段代碼,首先給 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 在下一輪再執行?,F在就有一個問題,怎么將函數添加到 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  }, })}

這樣,我們就一步一步的實現了所需的功能。完整的代碼和例子請戳。 

雖然經過了一些測試,但不保證沒有其它未知錯誤,歡迎提出問題。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
26uuu另类亚洲欧美日本老年| 精品国产一区二区三区久久| 亚洲va欧美va在线观看| 国产欧美va欧美va香蕉在线| 日韩av三级在线观看| 日韩精品在线私人| 成人精品久久久| 久久天天躁日日躁| 国产中文日韩欧美| 国产亚洲欧美一区| 国产精品美女主播在线观看纯欲| 91免费欧美精品| 亚洲成人激情视频| 日韩精品高清在线观看| 国产视频精品va久久久久久| 欧美精品久久久久久久久久| 在线观看久久久久久| 91精品久久久久久久久久久久久久| 久久精品99无色码中文字幕| 精品国偷自产在线视频| 欧美日韩国产成人高清视频| 国产亚洲视频在线观看| 国产成人av在线播放| xxav国产精品美女主播| 欧美在线一区二区视频| 91精品国产高清自在线看超| 久久色精品视频| 亚洲乱码国产乱码精品精| 国产精品久久久久久久久久久久| 国产91精品久久久| 国产精品久久久久久av福利| 精品亚洲精品福利线在观看| 久久久久久久久久久亚洲| 国产精品自产拍在线观| 成人福利视频在线观看| 亚洲精品不卡在线| 日韩av在线免费看| 久色乳综合思思在线视频| 国产婷婷成人久久av免费高清| 国产亚洲欧洲高清一区| 日日噜噜噜夜夜爽亚洲精品| 日韩av综合网站| 综合国产在线视频| 久久久久久久久久久网站| 日韩国产精品亚洲а∨天堂免| 日韩中文字幕在线免费观看| 成人国产精品色哟哟| 欧美美女操人视频| 国产91在线视频| 国产日本欧美一区二区三区| 日韩av在线网址| 日韩中文字幕国产| 亚洲成年人在线播放| 91免费精品视频| 亚洲精品视频二区| 欧美最近摘花xxxx摘花| 国产精品久久久久久久久久久久| 亚洲国产欧美一区二区三区久久| 欧美日韩国产丝袜另类| 国产成人综合一区二区三区| 青青草99啪国产免费| 成人在线视频网| 最新国产成人av网站网址麻豆| 色偷偷噜噜噜亚洲男人| 国产成人在线亚洲欧美| 亚洲人成电影网站色xx| 555www成人网| 国产精品福利小视频| 欧美极品欧美精品欧美视频| 欧美激情视频在线| 久久影视免费观看| 日韩av电影中文字幕| 亚洲999一在线观看www| 91在线色戒在线| 狠狠躁夜夜躁久久躁别揉| 欧美激情免费观看| 亚洲人成毛片在线播放| 日韩人体视频一二区| 操91在线视频| 欧美猛交ⅹxxx乱大交视频| 欧美国产日韩视频| 国外视频精品毛片| 久久久久久久久久久人体| 日韩免费在线视频| 亚洲一区二区免费在线| 一区二区三区四区精品| 欧美多人乱p欧美4p久久| 成人免费视频xnxx.com| 欧美午夜片欧美片在线观看| 97精品在线观看| 亚洲成人精品视频在线观看| 欧美在线视频免费| 日韩中文字幕在线| 久精品免费视频| 精品久久久久久中文字幕一区奶水| 午夜精品久久久久久久男人的天堂| 亚洲自拍偷拍区| 国产成人精品免高潮在线观看| 一区二区三区www| 欧美另类第一页| 亚洲亚裔videos黑人hd| 国产美女精品免费电影| 国产精品91免费在线| 国产精品久久久91| 日韩精品免费在线| 精品中文字幕视频| 一区二区成人av| 亚洲成人久久一区| 日韩电视剧免费观看网站| 亚洲xxxxx电影| 欧美精品福利在线| 亚洲国产精品嫩草影院久久| 北条麻妃99精品青青久久| 成人做爽爽免费视频| 自拍偷拍亚洲区| 欧美一级高清免费播放| 超碰97人人做人人爱少妇| 日韩欧美国产一区二区| 亚洲一区二区精品| 欧美精品九九久久| 日韩国产高清视频在线| 欧美亚洲在线观看| 在线观看视频99| 欧美国产中文字幕| 亚洲第一级黄色片| 久久久精品影院| 成人久久精品视频| 欧美黄色小视频| 欧美日韩精品在线| 亚洲欧美日韩爽爽影院| 国产a∨精品一区二区三区不卡| 97免费中文视频在线观看| 成人激情视频免费在线| 国产精品久久久久久久久久免费| 亚洲精品欧美一区二区三区| 日韩在线观看网址| 色播久久人人爽人人爽人人片视av| 亚洲精品国产精品国自产观看浪潮| 久久精品视频在线观看| 成人午夜黄色影院| 精品福利在线看| 亚洲午夜久久久影院| 91精品中文在线| 欧美性资源免费| 亚洲欧美一区二区精品久久久| 亚洲精品一区在线观看香蕉| 国产精品永久免费观看| 欧美日韩国产精品一区二区不卡中文| 欧美xxxx14xxxxx性爽| 日韩中文字幕免费视频| 国产精品久久av| 国产日韩在线亚洲字幕中文| 亚洲福利视频久久| 日韩电影中文字幕av| 国产黑人绿帽在线第一区| 日本国产高清不卡| 亚洲综合色激情五月| 亚洲成年网站在线观看| 国产美女久久精品香蕉69| 日本久久久久久久久| 国产成人精品999| 亚洲国产精品久久精品怡红院| 成人欧美一区二区三区在线| 欧美色欧美亚洲高清在线视频|