Wechat applet does not officially provide watch, which is used to monitor the change of attributes in data

Daily development without Watch will be constrained

ES5 provides a method called Object.defineProperty(), which hijabs the Object through getter/setter. The setter method is called during assignment and the corresponding method in watch is executed to implement listening.

Setting listeners

The setWatcher function traverses all properties in data, adding a listener observe to none of them

const setWatcher = (page) = > {
  const data = page.data
  const watch = page.watch
  Object.keys(watch).forEach(key= > {
    let targetData = data
    const targetKey = key
    const watchFun = watch[key].handler || watch[key]
    const deep = watch[key].deep
    observe(targetData, targetKey, watchFun, deep, page)
  })
}
Copy the code

Implementation listener

The observe function accepts five arguments

  • The data in paga
  • The property key in data
  • Functions in Watch
  • Whether to deep listen for attributes in data
  • Page is passed to the listener function

If deep is true, call observe recursively to perform deep listening

The setter method of Object.defineProperty is used to intercept external processing of data, while watchFn is called to implement the listening.

WatchFn takes two arguments, value and oldValue, and passes in the page using call. Make page(this) available in Watch

/** ** @param {Object} obj // data * @param {String} key // Data attribute * @param {Fucntion} watchFun // Listen function * @param {Boolean} deep // Whether to deep listen * @param {Object} page // page */
const observe = (obj, key, watchFn, deep, page) = > {
  let oldVal = obj[key]
  if(oldVal ! = =null && typeof oldVal === 'object' && deep) {
    Object.keys(oldVal).forEach(item= > {
      observe(oldVal, item, watchFun, deep, page)
    })
  }
  Object.defineProperty(obj, key, {
    configurable: true.enumerable: true,
    set(value) {
      if (value === oldVal) return
      watchFn.call(page, value, oldVal)
      oldVal = value
    },
    get() {
      return oldVal
    }
  })
}
Copy the code

Matters needing attention:

  • Watch only listens for existing properties, and methods like pop() and push() of arrays don’t fire
  • Properties that were not originally in data will not fire if they are added dynamically later

The complete code

const observe = (obj, key, watchFun, deep, page) = > {
  let oldVal = obj[key]
  if(oldVal ! = =null && typeof oldVal === 'object' && deep) {
    Object.keys(oldVal).forEach(item= > {
      observe(oldVal, item, watchFun, deep, page)
    })
  }
  Object.defineProperty(obj, key, {
    configurable: true.enumerable: true,
    set(value: any) {
	  if (value === oldVal) return
      watchFun.call(page, value, oldVal)
      oldVal = value
    },
    get() {
      return oldVal
    }
  })
}

export const setWatcher = (page) = > {
  const data = page.data
  const watch = page.watch
  Object.keys(watch).forEach(key= > {
    let targetData = data
    const targetKey = key
    const watchFun = watch[key].handler || watch[key]
    const deep = watch[key].deep
    observe(targetData, targetKey, watchFun, deep, page)
  })
}
Copy the code

use

import {  setWatcher} from "/utils/watch.js"

Page({
  data: {
    age: 12.person: {
        name: 'uccs'
      }
  },
  onLoad() {
    setWatcher(this)},watch: {
    age(val) {
      console.log(val)
    },
    person: {
      deep: true,
      handler(val) {
        console.log(val)
      }
    }
  },
  onClick() {
    this.data.age = 18
    this.data.person.name = 'tiantian'}})Copy the code