乐趣区

关于vue.js:Vue-watch源码解析

  1. 每个 watch 属性会创立观察者(Watcher 实例), 依据属性名 (例如person.name) 生成获取属性值的办法并保留在观察者的 getter 变量上,属性值变动后的调用函数也会被保留在观察者的 cb 变量上。观察者初始化时调用 getter 函数,读取 watch 监听的数据(响应式数据),通过数据属性定义的 get 将观察者增加到值的依赖收集器中。
var Watcher = function Watcher (
  vm,
  expOrFn,
  cb,
  options,
  isRenderWatcher
) {
  this.vm = vm;
  ...
  this.cb = cb;
  ...
  if (typeof expOrFn === 'function') {this.getter = expOrFn;} else {this.getter = parsePath(expOrFn);// 依据属性名生成获取属性值的办法
    ...
  }
  this.value = this.lazy
    ? undefined
    : this.get();};
// 解析属性名
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
  }
}
  1. 当监听值发生变化时,将调用收集的 watch 观察者的更新办法,执行 queueWatcher(this),将观察者增加到队列(微工作)中,在nextTick 中执行观察者的 cb 办法(watch 属性值变动后的调用函数)。
Watcher.prototype.update = function update () {
  /* istanbul ignore else */
  if (this.lazy) {this.dirty = true;} else if (this.sync) {this.run();
  } else {queueWatcher(this);//
  }
};
function queueWatcher (watcher) {
  var id = watcher.id;
  if (has[id] == null) {has[id] = true;
    if (!flushing) {// 处于收集 watcher 状态,未执行 watcher 办法
      queue.push(watcher);// 将
    } else {// 正在执行收集的 watcher 的更新办法
      // if already flushing, splice the watcher based on its id
      // if already past its id, it will be run next immediately.
      var i = queue.length - 1;
      while (i > index && queue[i].id > watcher.id) { // 插入到以后正在解决的 wacher 前面,且正好 id 要大于后面 id
        i--;
      }
      queue.splice(i + 1, 0, watcher); // 依据 id 大小插入到未解决的 watch 外面
    }
    // queue the flush
    if (!waiting) {
      waiting = true;

      if (process.env.NODE_ENV !== 'production' && !config.async) {flushSchedulerQueue();
        return
      }
      nextTick(flushSchedulerQueue);
    }
  }
}
退出移动版