# Watcher

  1. Vue会为当前实例创建一个renderWathcer挂载在vm._watcher上。
  2. renderWacher
    • 会将每个创建的Wathcer添加到vm._watchers数组中。
    • computed创建的Watcherlazy属性设置为true。以来值触发更新时watcher.update(),会将Watcher的dirty值设置为true。当computed触发了 了它的computedGetter时就会通过watcher.evaluate()更新watcher.value的值
    • 普通Watcher触发更新都会通过queueWatcher(this)进入更新队列中,等待在异步任务中去触发watcher.update() -> watcher.run()
export default class Watcher {
  vm: Component;
  expression: string;
  cb: Function;
  id: number;
  deep: boolean;
  user: boolean;
  lazy: boolean;
  sync: boolean;
  dirty: boolean;
  active: boolean;
  deps: Array<Dep>;
  newDeps: Array<Dep>;
  depIds: SimpleSet;
  newDepIds: SimpleSet;
  before: ?: Function;
  getter: Function;
  value: any
  
  constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: Object,
    isRenderWatcher?: boolean // 用来更新渲染 dom 的 watcher
  ) {
    this.vm = vm;
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)
    
    if (options) {
      this.deep = !!options.deep
      this.user = !!options.user
      this.lazy = !!options.lazy // watcher 值是否缓存
      this.sync = !!options.sync
      // before钩子函数
      this.before = options.before
    } else {
      this.deep = this.user = this.lazy = this.sync = false
    }
    this.cb = cb
    this.id = ++uid // watcher id 自增
    this.active = true
    this.dirty = this.lazy
    this.deps = []
    this.newDeps = []
    this.depIds = new Set()
    this.newDepIds = new Set()
    this.expression = process.env.NODE_ENV !== 'production' ? exporFn.toString() : ''
    // expOrFn 可以是个函数 返回 this.data ...
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = noop
        // 省略错误处理
        // throw new Error(/* error */)
      }
    }
    this.value = this.lazy ? undefined : this.get()
  }
  
  // 执行 this.getter,获取需要监听的属性的值 ,并重新收集依赖
  // 为什么要重新更新收集依赖
  // 因为触发说明数据被更新了 但是被更新的数据虽然已经经过 observe,但去没有进行依赖收集。
  // 在更新页面时,会重新执行一次 render 函数,执行期间会触发读取操作,这时候进行依赖收集
  get () {
    pushTarget(this) // 就是进行 Dep.target = this 的操作,方便依赖收集
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      // doSomething...
    } finally {
      if (this.deep) {
        traverse(value)
      }
      // 清除Dep.target
      popTarget()
      this.cleanupDeps()
    }
    return value
  }
  
  // observe 中收集依赖 自身添加 dep 只是为了方便 teardown 时方便删除对应的 watcher
  addDep (dep: Dep) {
    // 去重,dep 已经存在则不重复添加
    const id = dep.id
    if (!this.newDepIds.has(id)) {
      // 缓存
      this.newDepsIds.add(id)
      this.newDeps.push(dep)
      // 避免在 dep 中添加重复wathcer
      if (!this.depIds.has(id)) {
        dep.addSub(this)
      }
    }
  }
  
  // 去除已经不依赖的 Dep
  cleanupDeps () {
    let i = this.deps.length
    while (i --) {
      const dep = this.deps[i]
      // 如果 dep 不存在 newDepsIds 中 则在对应的 dep 里删除当前 Watcher
      if (!this.newDepIds.has(dep.id)) {
        // dep 里的 remove 应该会将对应的 id 和 dep 在当前的 deps 与 depIDS ..中移除自己
        dep.removeSub(this)
      }
    }
    let tmp = this.depIds
    this.depIds = this.newDepIds
    this.newDepIds = tmp
    this.newDepIds.clear()
    tmp = this.deps
    this.deps = this.newDeps
    this.newDeps = tmp
    this.newDeps.length = 0
  }
  
  // 根据 options 来决定
  update () {
    if (this.lazy) { // computed 将 dirty 设置为 true 当 dirty 为 true 时会重新获取值
      this.dirty = true
    } else if (this.sync) { // 同步执行 一般为了性能不会设置 sync
      this.run()
    } else {
      // 放进 watcher 的异步队列里
      queueWatcher(this)
    }
  }
  
  // 非 computed 其实是通过调用此方法 来获取最新值
  run () {
    if (this.active) {
      const value = this.get()
      if (
        value !== this.value ||
        isObject(value) ||
        this.deep
      ) {
        const oldValue = this.value
        this.value = value
        
        // 如果是用户定义的 wathcer 则传入 newVal 和 oldVal
        if (this.user) {
          try {
            this.cb.call(this.vm, value, oldValue)
          } catch (e) {
            // doSomething...
          }
        } else {
          this.cb.call(this.vm, value, oldValue)
        }
      }
    }
  }
  
  // 当 Watcher 的 dirty 为 true 时 会调用该方法
  evaluate () {
    this.value = this.get()
    this.dirty = false
  }
  
  // 向所有的 deps 中添加当前 watcher
  depend () {
    let i = this.deps.length
    while (i--) {
      this.deps[i].depend()
    }
  }
  
  // 将当前 watcher 从所有 deps 中移除
  teardown () {
    if (this.active) {
      if (!this.vm._isBeingDestroyed) {
        remove(this.vm._watchers, this)
      }
    }
    let i = this.deps.length
    while (i--) {
      this.deps[i].removeSub(this)
    }
    this.active = false
  }
}