溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

watch和computed怎么在微信小程序中使用

發(fā)布時(shí)間:2021-03-26 16:54:12 來(lái)源:億速云 閱讀:192 作者:Leah 欄目:web開(kāi)發(fā)

本篇文章給大家分享的是有關(guān)watch和computed怎么在微信小程序中使用,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

先上測(cè)試代碼

<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) } })
 },
})

現(xiàn)在我們要實(shí)現(xiàn) watch 和 computed 方法,使得 test 變化時(shí),test1、test2、test3 也變化,為此,我們?cè)黾恿艘粋€(gè)按鈕,當(dāng)點(diǎn)擊這個(gè)按鈕時(shí),test 會(huì)改變。

watch 方法相對(duì)簡(jiǎn)單點(diǎn),首先我們定義一個(gè)函數(shù)來(lái)檢測(cè)變化:

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 函數(shù)傳入的對(duì)象,給每個(gè)鍵調(diào)用該方法

function watch(ctx, obj) {
 Object.keys(obj).forEach(key => {
  defineReactive(ctx.data, key, ctx.data[key], function(value) {
   obj[key].call(ctx, value)
  })
 })
}

這里有參數(shù)是 fn ,即上面 watch 方法里 test 的值,這里把該方法包一層,綁定 context。

接著來(lái)看 computed,這個(gè)稍微復(fù)雜,因?yàn)槲覀儫o(wú)法得知 computed 里依賴的是 data 里面的哪個(gè)變量,因此只能遍歷 data 里的每一個(gè)變量。

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)
}

詳細(xì)解釋下這段代碼,首先給 data 里的每個(gè)屬性調(diào)用 defineReactive 方法。接著計(jì)算 computed 里面每個(gè)屬性第一次的值,也就是上例中的 test2、test3。

computed(this, {
 test2: function() {
  return this.data.test.a + '2222222'
 },
 test3: function() {
  return this.data.test.a + '3333333'
 }
})

這里分別調(diào)用 test2 和 test3 的值,將返回值與對(duì)應(yīng)的 key 值組合成一個(gè)對(duì)象,然后再調(diào)用 setData() ,這樣就會(huì)第一次計(jì)算這兩個(gè)值,這里使用了 reduce 方法。但是你可能會(huì)發(fā)現(xiàn)其中這兩行代碼,它們好像都沒(méi)有被提到是干嘛用的。

 ctx.data.$target = function() {
  ctx.setData({ [next]: obj[next].call(ctx) })
 }
 
 ctx.data.$target = null

可以看到,test2 和 test3 都是依賴 test 的,這樣必須在 test 改變的時(shí)候在其的 setter 函數(shù)中調(diào)用 test2 和 test3 中對(duì)應(yīng)的函數(shù),并通過(guò) setData 來(lái)設(shè)置這兩個(gè)變量。為此,需要將 defineReactive 改動(dòng)一下。

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 因?yàn)榇藭r(shí) this.data 還沒(méi)更新
    setTimeout(() => {
     subs.forEach(sub => sub())
    }, 0)
   }
   val = newVal
  },
 })
}

相較于之前,增加了幾行代碼,我們聲明了一個(gè)變量來(lái)保存所有在變化時(shí)需要執(zhí)行的函數(shù),在 set 時(shí)執(zhí)行每一個(gè)函數(shù),因?yàn)榇藭r(shí) this.data.test 的值還未改變,使用 setTimeout 在下一輪再執(zhí)行。現(xiàn)在就有一個(gè)問(wèn)題,怎么將函數(shù)添加到 subs 中。不知道各位還是否記得上面我們說(shuō)到的在 reduce 里的那兩行代碼。因?yàn)樵趫?zhí)行計(jì)算 test1 和 test2 第一次 computed 值的時(shí)候,會(huì)調(diào)用 test 的 getter 方法,此刻就是一個(gè)好機(jī)會(huì)將函數(shù)注入到 subs 中,在 data 上聲明一個(gè) $target 變量,并將需要執(zhí)行的函數(shù)賦值給該變量,這樣在 getter 中就可以判斷 data 上有無(wú) target 值,從而就可以 push 進(jìn) subs,要注意的是需要馬上將 target 設(shè)為 null,這就是第二句的用途,這樣就達(dá)到了一石二鳥(niǎo)的作用。當(dāng)然,這其實(shí)就是 vue 里的原理,只不過(guò)這里沒(méi)那么復(fù)雜。

到此為止已經(jīng)實(shí)現(xiàn)了 watch 和 computed,但是還沒(méi)完,有個(gè)問(wèn)題。當(dāng)同時(shí)使用這兩者的時(shí)候,watch 里的對(duì)象的鍵也同時(shí)存在于 data 中,這樣就會(huì)重復(fù)在該變量上調(diào)用 Object.defineProperty ,后面會(huì)覆蓋前面。因?yàn)檫@里不像 vue 里可以決定兩者的調(diào)用順序,因此我們推薦先寫(xiě) computed 再寫(xiě) watch,這樣可以 watch computed 里的值。這樣就有一個(gè)問(wèn)題,computed 會(huì)因覆蓋而無(wú)效。

思考一下為什么?

很明顯,這時(shí)因?yàn)橹暗?subs 被重新聲明為空數(shù)組了。這時(shí),我們想一個(gè)簡(jiǎn)單的方法就是把之前 computed 里的 subs 存在一個(gè)地方,下一次調(diào)用 defineReactive 的時(shí)候看對(duì)應(yīng)的 key 是否已經(jīng)有了 subs,這樣就可以解決問(wèn)題。修改一下代碼。

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 因?yàn)榇藭r(shí) this.data 還沒(méi)更新
    setTimeout(() => {
     subs.forEach(sub => sub())
    }, 0)
   }
   val = newVal
  },
 })
}

以上就是watch和computed怎么在微信小程序中使用,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI