论如何监听一个对象某个属性的变化

往期

前言

本文分为入门和进阶两部分,建议有经验的读者直接阅读进阶部分。

本文主要参考了vue这一开源库,若读者阅读过它的源码可以直接跳过本文 :)

入门

关于访问对象的某个属性

既然是入门,还是先提一下Vue.prototype.$watch的几种用法

const vm = Vue({
  data() {
    return {
      b: true,
      o: { name: 'obj' },
      a: ['a', 'b', 'c'],
      odeep: {
        path: {
          name: 'obj deep',
          value: [],
        },
      },
    };
  },

  watch: {
    // 如果b的值改变了,打印改变前与改变后的值
    b(val, oldVal) {
      console.warn(val, oldVal);
    },
    // 如果o.name的值改变了,打印改变前与改变后的值
    'o.name': {
      handler(val, oldVal) {
        console.warn(val, oldVal);
      },
    },
  },

  created() {
    // 深度监听: 如果odeep.path.name/odeep.path.value的值改变了,打印odeep.path改变前与改变后的值
    this.$watch('odeep.path', (val, oldVal) => {
      console.warn(val, oldVal);
    }, { deep: true });
  },
});
复制代码

如何去通过诸如o.name的字符串访问到vm.o.name呢? vm['o.name']当然是不行的,需要写成vm['o']['name']这样的形式。

function parsePath(path) {
  if (!/[\w.]$/.test(path)) {
    // 为什么要返回一个带参数的函数呢? 提前告诉你,是为了触发被监听对象的get方法(还记得上一篇文章的内容吗)
    return function(obj) {};
  }

  const segs = path.split('.');
  // 想知道这里为什么不用forEach吗,试试在forEach里使用return吧
  return function(obj) {
    for (let i = 0; i < segs.length; i += 1) {
      if (!obj) {
        return;
      }

      obj = obj[segs[i]];
    }

    return obj;
  };
}

const obj = {
  o: { name: 'a' },
};
console.assert(parsePath('o.name')(obj) === 'a');
复制代码

关于观察者模式

先让我们看看维基百科是怎么说的:

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

也就是说subject用来维护依赖列表, 每个依赖都是一个observer。当依赖列表中的某一项发生了变化,就自动通知subject自身状态的变更。

让我们先拷贝一下上篇文章的内容, 注意注释里的内容!

function defineReactive(obj, key, val) {
  if (isPlainObject(val)) {
    observe(val);
  } else if (Array.isArray(val)) {
    dealAugment(val, dep);
    observeArray(val);
  }

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      // 将依赖加入依赖列表
      return val;
    },

    set(newVal) {
      if (val !== newVal) {
        val = newVal;
        if (isPlainObject(newVal)) {
          observe(newVal);
        } else if (Array.isArray(newVal)) {
          dealAugment(newVal, dep);
          observeArray(newVal);
        }
        // 依赖通知subject自身状态的改变,即调用callback
      }
    },
  });
}
复制代码

但是callback在$watch函数中,如何传递给依赖, 并在被监听对象该属性变化时调用呢?

我们可以利用一个全局变量(在这里我们称它为DepTarget),在访问变量的时候设置为$watch函数的callback, 并将这个callback存到一个集合里,访问结束后置空。同时需要注意的是,

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值