Watcher类

1. Watcher类创建

在编写Watcher类之前,我们先来看一张图,理解一下DepWatcher的关系

通过前面的学习,我们知道在Observer类中为每一个响应式的数据创建了Dep对象,而且在getter 中会收集依赖,所谓收集依赖就是将watcher观察者添加到subs数组中.

而在setter中会触发依赖,其实就是调用Dep对象中notify方法,该方法会获取subs数组中的所有的watcher,然后执行watcher中的update方法来更新对应的视图。

Watcher 类的代码如下:

class Watcher {
  constructor(vm, key, cb) {
    this.vm = vm;
    //data中的属性名称
    this.key = key;
    //回调函数负责更新视图
    this.cb = cb;
    //获取更新前的旧值
    this.oldValue = vm[key];
  }
  // 当数据发生变化的时候更新视图
  update() {
    //只要update方法调用,获取到的值就是新值,因为当数据发生了变化,才会调用该方法
    let newValue = this.vm[this.key];
    if (newValue === this.oldValue) {
      return;
    }
    //调用cb回调函数更新视图,将新值传递到该回调函数中
    this.cb(newValue);
  }
}

接下来还有一件事情需要处理一下:

当创建了·Watcher对象后,需要将当前创建的Watcher对象添加到Dep中的subs数组中。

我们可以查看Observer类,在get方法中已经写过将Watcher对象添加到Dep中的subs数组中了(Dep.target && dep.addSub(Dep.target);),但是

问题是,我们并没有创建target属性,所以下面我们创建一下target属性。

下面在Watcher类的构造方法中,添加给Dep添加target属性,用来保存Watcher的实例。

class Watcher {
  constructor(vm, key, cb) {
    this.vm = vm;
    //data中的属性名称
    this.key = key;
    //回调函数负责更新视图
    this.cb = cb;
    // 把watcher对象记录添加到Dep类的静态属性target上.
    Dep.target = this;
    //触发get方法,因为在get方法中会调用addSub方法(下面我们通过vm来获取key对应的值的时候,就执行了get方法,因为我们已经将data属性编程了响应式,为其添加了`getter/setter`).
    //获取更新前的旧值
    this.oldValue = vm[key];
    Dep.target = null; //防止以后重复性的添加
  }
}

以上内容需要重点去体会.

2. 创建Watcher对象

下面来看一下关于Watcher对象的创建。

// 编译文本节点,处理差值表达式
  compileText(node) {
    // console.dir(node);
    // {{ msg }}
    //我们是用data中的属性值替换掉大括号中的内容
    let reg = /\{\{(.+)\}\}/;
    //获取文本节点的内容
    let value = node.textContent;

    //判断文本节点的内容是否能够匹配正则表达式
    if (reg.test(value)) {
      //获取插值表达式中的变量名,去掉空格($1 表示获取第一个分组的内容。)
      let key = RegExp.$1.trim();
      //根据变量名,获取data中的具体值,然后替换掉差值表达式中的变量名.
      node.textContent = value.replace(reg, this.vm[key]);
      //创建Watcher对象,当数据发生变化后,更新视图
      new Watcher(this.vm, key, (newValue) => {
          //newValue是更新后的值
        node.textContent = newValue;
      });
    }
  }

下面要在index.html文件中导入相关的js文件。

<script src="./js/dep.js"></script>
    <script src="./js/watcher.js"></script>
    <script src="./js/compiler.js"></script>
    <script src="./js/observer.js"></script>
    <script src="./js/vue.js"></script>

注意:以上导入文件的顺序,由于在watcher.js文件中使用了dep.js文件中的内容,所以先导入dep,同样在compiler.js文件中使用了watcher.js文件中内容,所以先导入了watcher.js.

下面可以进行测试了。

先将index.html文件中的,如下语句注释掉:

 vm.msg = { text: "abc" };

然后,打开浏览器的控制台,输入如下内容

vm.msg="abc"

对应的页面视图中的内容也发生了变化。这也就实现了响应式机制,所谓响应式就是当数据变化了,对应的视图也会进行更新。

所以需要在textUpdatermodelUpdater方法中完成Watcher对象的创建。

 //处理v-text指令
  textUpdater(node, value, key) {
    node.textContent = value;
    new Watcher(this.vm, key, (newValue) => {
      node.textContent = newValue;
    });
  }
  //处理v-model
  modelUpdater(node, value,key) {
    //v-model是文本框的属性,给文本框赋值需要通过value属性
    node.value = value;
    new Watcher(this.vm, key, (newValue) => {
      node.value = newValue;
    });
  }
  update(node, key, attrName) {
    //根据传递过来的属性名字拼接Updater后缀获取方法。
    let updateFn = this[attrName + "Updater"];
    updateFn && updateFn.call(this, node, this.vm[key], key); //注意:传递的是根据指令的值获取到的是data中对应属性的值。
  }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Seric.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值