【源码阅读笔记】vue2(二)—— Dep

43 阅读2分钟

这篇文章阅读Dep的实现。
文件路径:src\core\observer\dep.ts。

class Dep

dep的实现内容相对比较少,文件中主要实现了Dep类以及target栈的维护。

export default class Dep {
  //保存watcher
  static target?: DepTarget | null
  //dep的id
  id: number
  //收集依赖的数组容器
  subs: Array<DepTarget | null>
  // pending subs cleanup
  _pending = false

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

  //向sub数组中存储依赖
  addSub(sub: DepTarget) {
    this.subs.push(sub)
  }

  //移除依赖
  removeSub(sub: DepTarget) {
    // #12696 deps with massive amount of subscribers are extremely slow to
    // clean up in Chromium
    // to workaround this, we unset the sub for now, and clear them on
    // next scheduler flush.
    this.subs[this.subs.indexOf(sub)] = null
    if (!this._pending) {
      this._pending = true
      pendingCleanupDeps.push(this)
    }
  }

  //存储依赖
  //调用watcher的addDep方法,在addDep中调用addSub
  depend(info?: DebuggerEventExtraInfo) {
    if (Dep.target) {
      Dep.target.addDep(this)
      if (__DEV__ && info && Dep.target.onTrack) {
        Dep.target.onTrack({
          effect: Dep.target,
          ...info
        })
      }
    }
  }

  //通知更新
  notify(info?: DebuggerEventExtraInfo) {
    // stabilize the subscriber list first
    const subs = this.subs.filter(s => s) as DepTarget[]
    if (__DEV__ && !config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    //遍历subs数组 执行每个watcher的update函数
    for (let i = 0, l = subs.length; i < l; i++) {
      const sub = subs[i]
      if (__DEV__ && info) {
        sub.onTrigger &&
          sub.onTrigger({
            effect: subs[i],
            ...info
          })
      }
      sub.update()
    }
  }
}

总结,Dep类主要包含dep的id、用于存储依赖的的数组、收集依赖的方法、移除依赖的方法以及派发更新的方法。

target栈

先看代码

//全局静态变量赋值
//指向当前解析的watcher
Dep.target = null 
//target栈
const targetStack: Array<DepTarget | null | undefined> = []
//watcher入栈
export function pushTarget(target?: DepTarget | null) {
  targetStack.push(target)
  Dep.target = target
}
//watcher出栈
export function popTarget() {
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}

dep文件中还定义了一个全局唯一静态变量Dep.target用来存取watcher。以及一个栈结构的数组targetStack用来存储watcher。还有两个全局函数 pushTarget 和 popTarget。
targetStack是Vue2中才引入的机制,而Vue1中则是仅靠Dep.target来进行依赖收集。因为Vue2引入了虚拟Dom。Vue2中视图被抽象js对象通过render函数来渲染,一个render函数就代表一个Vue组件也就对应了一个watcher,函数自然会存在嵌套的概念,便需要结构来维护watcher。