vue源码分析(十四)核心函数之Watcher

2020-04-21  本文已影响0人  vue爱好者

我们打开文件 src/core/observer/watcher.js
由于文件代码量过多,我们一一来看,就不一次性帖出来了, Watcher类主要有如下方法:

constructor 初始化构造函数
get 计算getter
addDep 添加依赖项
cleanupDeps 清理依赖项
update 依赖项更改时调用
run 调用处理函数
evaluate 重新计算值,这只适用于懒惰的观察者
depend 调用Dep的depend函数
teardown 从所有依赖项的订阅服务器列表中移除

我们接下来看几个主要的,简单的我们就略过了:

constructor

constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)

    // 省略部分代码...

   // 表达式是否是函数
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = noop
        process.env.NODE_ENV !== 'production' && warn(
          `Failed watching path: "${expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }
    this.value = this.lazy
      ? undefined
      : this.get()
  }

可以看到有对 expOrFn 参数进行类型判断,因为他可能有两种类型 expOrFn: string | Function 字符串和函数。
接下来如果 this.lazy为真就给value赋值为 'undefined',否则就调用get函数进行处理。

get

 /**
   * Evaluate the getter, and re-collect dependencies.
   */
  get () {
    //入栈操作
    pushTarget(this)
    let value
    const vm = this.vm
    try {
     // 调用 'constructor '处理好的getter函数
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      // 如果'deep'为真,就对value进行一个深度监听。
      if (this.deep) {
        traverse(value)
      }
      //出栈操作
      popTarget()
     // 清理依赖项
      this.cleanupDeps()
    }
    return value
  }

可以看到首先进行了一个 入栈操作pushTarget(this),然后调用 constructor 里面处理好的 getter 函数,完成之后判断 deep 是否 为真,就对value进行一个深度监听。紧接着就是出栈操作。
最后 调用 this.cleanupDeps() 清理依赖项 。

update

/**
   * Subscriber interface.
   * Will be called when a dependency changes.
   */
  update () {
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      queueWatcher(this)
    }
  }

1、如果 lazy 属性为真就把 dirty 标识为真。
2、如果 sync 属性为真,就调用run方法。
3、否则就调用 queueWatcher 函数,把this推入到列队里面。

run

/**
   * Scheduler job interface.
   * Will be called by the scheduler.
   */
  run () {
    if (this.active) {
      const value = this.get()
      if (
        value !== this.value ||
        // Deep watchers and watchers on Object/Arrays should fire even
        // when the value is the same, because the value may
        // have mutated.
        isObject(value) ||
        this.deep
      ) {
        // set new value
        const oldValue = this.value
        this.value = value
        if (this.user) {
          try {
            this.cb.call(this.vm, value, oldValue)
          } catch (e) {
            handleError(e, this.vm, `callback for watcher "${this.expression}"`)
          }
        } else {
          this.cb.call(this.vm, value, oldValue)
        }
      }
    }
  }

首先判断 active 是否为真,默认就是 true,如果是false的话,那就是移除了监听器,或者组件被移除了(调用了destroy生命周期)。

(active=false代表已被销毁),调用unwatcher和触发destroy都会销毁active

紧接着调用了get方法,获取最新的value值,然后判断最新的值是否和旧值相等,或者最新的值是object对象,或者存在deep选项。
最后调用 cb函数(一般是用户自定义hanle方法,或者vue自带的一个空函数) 处理。

上一篇下一篇

猜你喜欢

热点阅读