小程序对某个对象实现深度监听

场景

  • 有一个与页面实例不关联的外部对象,当其某个属性改变时,需要页面做出反应,也就是页面需要监听它

问题

  • 肯定直接想到计算属性,但是小程序不自带,然后用wx-computed等插件,发现都有缺陷,只能对页面实例内部data的属性进行监听
  • 然而就算把这个外部对象声明到page的data里,但由于对象属性的改变不是响应式的,这在vue里还能通过$set来解决,但是没有$set就只能想办法自己实现对该外部对象的深度监听

解决

  • 在app.js中定义全局的监听函数

    App({
      onLaunch: function () {
        console.log('app running')
      },
      // 设置监听器
      // ctx表示上下文,aim表示监听的目标对象,一般情况非深度监听没有aim参数,而是直接对ctx监听
      // keys表示监听的目标对象的目标属性,其值是一个函数,也就是监听到其变化后触发的回调,然后call ctx给回调
      watch: function (ctx, aim, keys) {
        Object.keys(keys).forEach(key => {
          this.observer(aim, key, aim[key], function (value) {
            keys[key].call(ctx, value)
          })
        })
      },
      // 监听属性,并执行监听函数
      observer: function (data, key, val, callback) {
        Object.defineProperty(data, key, {
          configurable: true,
          enumerable: true,
          get: function () {
            return val
          },
          // 重写set函数,就能在其属性改变时触发回调
          set: function (newVal) {
            if (newVal === val) return
            callback && callback(newVal)
            val = newVal
          },
        })
      }
    })
    
  • 然后在需要使用的page实例中引入

    const app = getApp()
    // 这里的上下文直接传this,Obj就是需要监听的外部对象,监听的属性是objProperty,然后页面在回调里做出响应
    app.watch(this, Obj, {
      objProperty: function (newVal) {
        if (newVal) {
          this.setData({
            pageVar: newVal
          })
        }
      }
    })
    
  • 但是这里笔者也没有实现完全深度监听,比如上面监听的objProperty如果又是一个对象,就又出现了同样的问题,objProperty的某个属性改变不是响应式的,这时候就要在watch的定义里,对aim[key]判断是否是对象,如果是,需要递归一下,就能完全深度监听了

posted @ 2023-05-08 10:19  Mizuki-Vone  阅读(164)  评论(0编辑  收藏  举报