Vue源码学习之双向数据绑定

1.什么是双向数据绑定?

单向绑定非常简单,就是把Model绑定到View,当我们用JavaScript代码更新Model时,View就会自动更新。有单向数据绑定就有双向数据绑定。那什么是双向数据绑定呢?如果用户更新了View,Model的数据也自动被更新了,这种情况就是双向数据绑定,如图:

 2.双向数据绑定一般应用在什么场景?

双向数据绑定一般应用在表单中,用来提交数据。其他基本上是使用单向绑定就可以了

3.双向数据绑定是如何实现的?

在Vue中双向数据绑定是通过数据劫持结合发布者-订阅者模式来实现的。这个时候问题又来了,具体是如何实现的呢?

双向数据绑定关键点在于我们如何知道数据发生变化,当我们知道数据何时发生改变,只要数据数据发生改变,我们就去通知更新视图就可以了

在JS中为我们提供了Object.defineProperty方法,在Object.defineProperty方法,为我们提供了get和set方法,通过get方法我们可以监听到数据赋值给了谁,通过set方法我们可以监听到数据修改。

当我们通过Object.defineProperty方法知道数据什么时候发生改变,那么当数据发生改变我们去通知视图就可以了。但是问题又来了,视图那么大,我们该通知谁去改变呢?

当a用到了b,那么b发生改变的时候我们需要通知a。“你依赖的b发生了改变,你需要更新了”这个过程就叫依赖收集

那么我们什么时候需要依赖收集,什么时候需要依赖更新呢?

在Object.defineProperty方法中提供了get,set方法。get可以监听到数据赋值给了谁,set监听到数据的修改,那么我们就可以在get方法中收集依赖,set方法中通知依赖更新

当我们知道在get方法中收集依赖,那么我们又该如何收集依赖呢?

这个时候时候依赖管理器Dep就产生了, Dep类的源码如下:

export default class Dep {
  constructor () {
    this.subs = []
  }

  addSub (sub) {
    this.subs.push(sub)
  }
  // 删除一个依赖
  removeSub (sub) {
    remove(this.subs, sub)
  }
  // 添加一个依赖
  depend () {
    if (window.target) {
      this.addSub(window.target)
    }
  }
  // 通知所有依赖更新
  notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

在Dep类中实例了几个方法对依赖进行添加,删除和通知依赖更新的操作,我们有了依赖管理器后,我们就可以在getter中收集依赖,在setter中通知依赖更新了

现在我们知道可以知道了什么是依赖,什么时候收集依赖,何时通知依赖更新,那么这个依赖到底是谁呢,代码中如何描述呢?

这个时候Vue中为我们提供了Watcher类,如下:

export default class Watcher {
  constructor (vm,expOrFn,cb) {
    this.vm = vm;
    this.cb = cb;
    this.getter = parsePath(expOrFn)
    this.value = this.get()
  }
  get () {
    window.target = this;
    const vm = this.vm
    let value = this.getter.call(vm, vm)
    window.target = undefined;
    return value
  }
  update () {
    const oldValue = this.value
    this.value = this.get()
    this.cb.call(this.vm, this.value, oldValue)
  }
}

什么是Watcher类呢,Watcher类的实例就是上面说的那个谁了,谁用到了数据,谁就是依赖,我们就为谁创建一个Watcher类

收集依赖的代码如下:

function defineReactive (obj,key,val) {
  if (arguments.length === 2) {
    val = obj[key]
  }
  if(typeof val === 'object'){
    new Observer(val)
  }
  const dep = new Dep()  //实例化一个依赖管理器,生成一个依赖管理数组dep
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get(){
      dep.depend()    // 在getter中收集依赖
      return val;
    },
    set(newVal){
      if(val === newVal){
          return
      }
      val = newVal;
      dep.notify()   // 在setter中通知依赖更新
    }
  })
}

4.不足之处

虽然我们通过Object.defineProperty实现了双向数据绑定,但是这个方法只能观测到Object数据的取值和设置值。

当然vue也为我们提供了两个全局的方法Vue.set和Vue.delete来解决问题

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

✎﹏ℳ๓敬坤

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

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

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

打赏作者

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

抵扣说明:

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

余额充值