# watch

export function initState(vm: Component) {
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch);
  }
}

初始化watch也是在initState中,这里做了个判断是为了去除 firfox中的原生 watch 方法。

function initWatch(vm: Component, watch: Object) {
  for (const key in watch) {
    const handler = watch[key];
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i]);
      }
    } else {
      createWatcher(vm, key, handler);
    }
  }
}

这里会对watch遍历,获取到handler,之后调用createWatcher,如果是数组,对每一个子元素进行调用。

function createWatcher(
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  if (isPlainObject(handler)) {
    options = handler;
    handler = handler.handler;
  }
  if (typeof handler === "string") {
    handler = vm[handler];
  }
  return vm.$watch(expOrFn, handler, options);
}

createWatcher函数主要是调用$watch函数,在调用之前会做一层处理,因为我们定义 watch 的时候可能传函数或者对象,所以做了一层判断获取到用户定义的函数。

// 如果传递的是对象,
// options={
//   deep: true,
//   handler() {},
// }
// handler=function(){}
export default {
  watch: {
    name: {
      deep: true,
      handler() {},
    },
  },
};
// 如果传递的是字符串,则会从实例vm中获取到对应的methods
// handler=function method1(){}
export default {
  watch: {
    name: "method1",
  },
};

$watch定义在stateMixin中:

Vue.prototype.$watch = function (
  expOrFn: string | Function,
  cb: any,
  options?: Object
): Function {
  // 需要获取实例,所有没有静态方法
  const vm: Component = this;
  if (isPlainObject(cb)) {
    return createWatcher(vm, expOrFn, cb, options);
  }
  options = options || {};
  // 用户watcher
  options.user = true;
  const watcher = new Watcher(vm, expOrFn, cb, options);
  if (options.immediate) {
    const info = `callback for immediate watcher "${watcher.expression}"`;
    pushTarget();
    invokeWithErrorHandling(cb, vm, [watcher.value], vm, info);
    popTarget();
  }
  // 取消监听
  return function unwatchFn() {
    watcher.teardown();
  };
};

$watch函数也很简单,就是创建了watcher实例,传递了user=true,如果有immediate参数,则执行传递过来的cb,最后返回unwatchFn函数。

class Watcher {
  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;
    }
  }
}

deps数组中移除当前的watcher