【core/observer】之Dep类

/* @flow */

import type Watcher from './watcher'
import { remove } from '../util/index'
import config from '../config'

let uid = 0

/**
 * 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)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    // 首先确定订阅列表
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      // subs如果不是异步执行的就不是按顺序的
      // 我们需要对他们排序,以确保它们以正确的顺序触发
      subs.sort((a, b) => a.id - b.id)
    }
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

// 当前正在评估的观察目标
// 这是全局唯一的target,因为一个时刻内,
// 就只会有一个观察者函数在执行
Dep.target = null
const targetStack = []

export function pushTarget (target: ?Watcher) {
  targetStack.push(target)
  Dep.target = target
}

export function popTarget () {
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}

Dep类

先来聊聊观察者模式:
观察者模式是一种实现一对多关系解耦的行为设计模式。它主要涉及两个角色:观察目标观察者
观察者要直接订阅观察目标,观察目标一做出通知,观察者就要进行处理

Vue源码中实现依赖收集,实现了三个类:

  • Dep:扮演观察目标的角色,每一个数据都会有Dep类实例,它内部有个subs队列,subs就是subscribers的意思,收集依赖数据的观察者,当数据变更时,调用dep.notify()通知观察者 。
  • Watcher:扮演观察者的角色,进行观察者函数的包装处理。
  • Observer:辅助的可观测类,数组/对象通过它的转化,可成为可观测数据。

Dep类就是用来收集和触发依赖的地方,所有的依赖都放在subs队列里。

收集依赖

addSub (sub: Watcher) {
  this.subs.push(sub)
}
depend () {
  if (Dep.target) {
    Dep.target.addDep(this)
  }
}

之前,在之前的defineReactive方法的在getter里的时候,会触发dep.depend()方法在这里插入图片描述
这就是把target的依赖放进subs队列里,存储起来
调用depend()的时候,不直接把Dep.target加入dep.subs,而是调用了Dep.target.addDep ,为了保证dep.subs里的每个watcher都是唯一,所以才不直接把当前watcher塞入dep.subs里。所以depend方法其实就是调用了watcher里的addDep方法。

触发依赖

notify () {
  // 首先确定订阅列表
  const subs = this.subs.slice()
  if (process.env.NODE_ENV !== 'production' && !config.async) {
    // subs如果不是异步执行的就不是按顺序的
    // 我们需要对他们排序,以确保它们以正确的顺序触发
    subs.sort((a, b) => a.id - b.id)
  }
  for (let i = 0, l = subs.length; i < l; i++) {
    subs[i].update()
  }
}

setter里执行完成值变更后,通知watcher进行更新。setter能在父级里访问到dep,所以就能获得dep.subs,就知道有哪些watcher依赖于当前数据,值更新后,通过调用dep.notify(),来遍历dep.subs里的watcher,执行每个watcher的update()方法,让每个watcher进行更新。

Dep.target

JavaScript是单线程,在vue中,虽然有多个观察者函数(watcher),但是一个时刻内,就只会有一个观察者函数在执行,那么此刻正在执行的那个观察者函数,所对应的Watcher实例,便会被赋给Dep.target这一类变量,从而只要访问Dep.target就能知道当前的观察者是谁。

// 当前正在评估的观察目标
// 这是全局唯一的target,因为一个时刻内,
// 就只会有一个观察者函数在执行
Dep.target = null
const targetStack = []

export function pushTarget (target: ?Watcher) {
  targetStack.push(target)
  Dep.target = target
}

export function popTarget () {
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值