Vue2 源码解析-响应式系统 Watch

44 阅读3分钟

 function initState(vm) {
     ...
    if (opts.watch && opts.watch !== nativeWatch) {
    // 初始化
      initWatch(vm, opts.watch);
    }
  }
  
   function initWatch(vm, watch) {
    for (var key in watch) {
      var handler = watch[key];
      //如果 handler 是一个数组,那么就会对数组中的每个元素都调用 createWatcher 函数创建观察者。
      //如果 handler 是一个函数,那么就只会对它调用一次 createWatcher 函数。
      if (Array.isArray(handler)) {
        for (var i = 0; i < handler.length; i++) {
          createWatcher(vm, key, handler[i]);
        }
      } else {
        createWatcher(vm, key, handler);
      }
    }
  }
  
  function createWatcher(vm, expOrFn, handler, options) {
    if (isPlainObject(handler)) {
    //先检查 handler 是否为选项对象。如果是,则将 options 赋值为 handler,
    //并将 handler 赋值为选项对象的 handler 属性。
      options = handler;
      handler = handler.handler;
    }
    if (typeof handler === "string") {
    //然后,检查 handler 是否为字符串。如果是,则将 handler 赋值为 vm 实例的对应属性
      handler = vm[handler];
    }
    return vm.$watch(expOrFn, handler, options);
  }
  
  Vue.prototype.$watch = function (expOrFn, cb, options) {
      var vm = this;
      if (isPlainObject(cb)) {
        return createWatcher(vm, expOrFn, cb, options);
      }
      options = options || {};
      options.user = true;
      // expOrFn : 一个字符串或函数,表示要监听的表达式或函数
      // cb : watch的回调
      // options : 一个对象,包含一些可选的配置项
      var watcher = new Watcher(vm, expOrFn, cb, options);
      if (options.immediate) {
      //如果 `options.immediate` 为 true,则立即执行回调函数
        try {
          cb.call(vm, watcher.value);
        } catch (error) {
          handleError(
            error,
            vm,
            'callback for immediate watcher "' + watcher.expression + '"'
          );
        }
      }
      //最后返回一个函数,用于取消监听
      return function unwatchFn() {
        watcher.teardown();
      };
    };
   // 函数 `parsePath` 首先使用 `bailRE` 正则表达式测试输入的 `path` 参数是否合法,
   //如果不合法就直接返回。
   // 如果 `path` 合法,函数会将 `path` 按照 . 进行分割,得到一个数组 `segments`。
   //然后返回一个新的函数,这个函数会循环遍历 `segments` 数组,每次取出一个元素,
   //用它来访问 `obj` 对象的一个属性,并将访问的结果赋值给 `obj`,
   //直到遍历完整个 `segments` 数组。最后,返回最内层的属性值。
   function parsePath(path) {
    if (bailRE.test(path)) {
          return;
    }
    var segments = path.split(".");
    return function (obj) {
      for (var i = 0; i < segments.length; i++) {
        if (!obj) {
          return;
        }
        obj = obj[segments[i]];
      }
      return obj;
    };
  }
  
  function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
    ...
     if (typeof expOrFn === "function") {
      this.getter = expOrFn;
    } else {
    // 得到访问依赖项的函数
      this.getter = parsePath(expOrFn);
      if (!this.getter) {
        this.getter = noop;
        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();
    
  Watcher.prototype.get = function get() {
    pushTarget(this);

    var value;
    var vm = this.vm;
    try {
    // 进行依赖收集
      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
      if (this.deep) {
        traverse(value);
      }
      popTarget();
      this.cleanupDeps();
    }
    return value;
  };
  
  // deep处理
  function traverse(val) {
  //seenObjects 是一个 Set 类型的变量,其中存储了当前已经遍历过的对象。
  //这个 Set 用于避免同一个对象被重复遍历,保证遍历过程中不会产生死循环。
    _traverse(val, seenObjects);
    seenObjects.clear();
  }
  
  function _traverse(val, seen) {
    var i, keys;
    var isA = Array.isArray(val);
    if (
      (!isA && !isObject(val)) ||
      Object.isFrozen(val) ||
      val instanceof VNode
    ) {
    //首先检查给定的值是否是数组、是否是对象、是否被冻结、
    //是否是 VNode(Vue.js 中的虚拟 DOM 元素),如果满足任意一种情况就退出函数。
      return;
    }
    if (val.__ob__) {
    //它会检查值是否有一个名为 __ob__ 的属性,并且这个属性有一个名为 dep 的属性,
    //如果有,就检查 seen 参数(这是一个 Set)是否已经包含了 dep.id,如果包含了就退出函数
      var depId = val.__ob__.dep.id;
      if (seen.has(depId)) {
        return;
      }
      seen.add(depId);
    }
    // 执行到该步骤时还在get函数中,此时访问属性会将watch的回调函数作为依赖进行收集
    if (isA) {
    //检查给定的值是否是数组,如果是,就遍历数组中的所有元素并递归调用自身
      i = val.length;
      while (i--) {
        _traverse(val[i], seen);
      }
    } else {
      keys = Object.keys(val);
      i = keys.length;
      while (i--) {
      //遍历对象中的所有属性并递归调用自身
        _traverse(val[keys[i]], seen);
      }
    }
  }
  
  // 派发更新时执行
   Watcher.prototype.run = function run() {
    if (this.active) {
      var 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
        var oldValue = this.value; //旧值
        this.value = value;
        // watcher
        if (this.user) {
          try {
            this.cb.call(this.vm, value, oldValue); // watcher回调函数
          } catch (e) {
            handleError(
              e,
              this.vm,
              'callback for watcher "' + this.expression + '"'
            );
          }
        } else {
          this.cb.call(this.vm, value, oldValue);
        }
      }
    }
  };