Vue源码剖析(二)响应式

响应式

网上看了好多的博客和源码教程,感觉很多仔细的地方没有说清,而且在一些复杂的部分加了好多的描述,所以就想自己也写下心得, 方便自己, 方便他人,有兴趣的同学可以关注我的github里面有我之前一些博文 github/193Eric

主要是双向绑定和依赖收集


Object.defineProperty,Vue.js就是基于它实现「响应式系统」的。

observer(可观察的)

我们知道要实现双向绑定需要给对象通过Object.defineProperty添加getter和setter方法。那我们是怎么来设置vue的呢,我们可以实现一个observer函数,我们需要它接收一个对象,那个对象是需要对数据进行响应式的,然后遍历该对象的所有属性设置Object.defineProperty。


依赖收集

  • 订阅者Dep (源码)
export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  /*添加一个观察者对象*/
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  /*移除一个观察者对象*/
  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  /*依赖收集,当存在Dep.target的时候添加观察者对象*/
  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  /*通知所有订阅者*/
  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

/*依赖收集完需要将Dep.target设为null,防止后面重复添加依赖。*/
Dep.target = null
  • 观察者Watcher
class Watcher {
    constructor () {
        /* 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到 */
        Dep.target = this;
    }

    /* 更新视图的方法 */
    update () {
        console.log("视图更新啦~");
    }
}

Dep.target = null;

// ps 注意这个target这个在源码里面有特殊的应用。

通过上面两个对象,我们可以看到订阅者和观察者,然后我们来定义一个defineProperty

function defineReactive (obj, key, val) {
    /* 一个Dep类对象 */
    const dep = new Dep();
    
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
            if (Dep.target) {
              /*进行依赖收集*/
              dep.depend()   
            }     
        },
        set: function reactiveSetter (newVal) {
            if (newVal === val) return;
            /* 在set的时候触发dep的notify来通知所有的Watcher对象更新视图 */
            dep.notify();
        }
    });
}

注意dep.depend()的条件,这是因为在第一次初始化的时候Dep.target为null,所以不会进行依赖收集,一种是当通过生成reder funciton的时候(具体看第一章),会生成一个Watcher,同时会触发一次getter,然后加入依赖收集,另一种是computed(计算属性)调用的时候会会生成一个Watcher,同时会触发一次getter,然后加入依赖收集,整个vue项目里面只有这两种时候可以加入依赖收集。

然后之后我们就知道每一次修改对象状态的时候,都会触发setter方法,然后通过dep来通知加入依赖收集的watcher进行遍历_update()修改,然后通过diff算法 ,对比渲染到页面。

// 划重点,watcher是怎么设置Dep.target的,是通过生成watcher的时候,对全局的Dep对象加入了个target==_watcher,然后在dep.depend()的时候,把_watcher加入到闭包生成的dep数组里面。(这里面有一个Dep,和一个闭包dep)

猜你喜欢

转载自blog.csdn.net/qq_24073885/article/details/102773516