理解vue2中的watch监听

  1. vue实例组件初始化过程中,在执行initState(vm)方法初始化状态时,判断options.watch有值时会进行initWatch(vm, options.watch)处理,然后对watch对象中的每个watch属性执行createWatcher方法
javascript 复制代码
function initState(vm) {
  // 传入的watch
  if (options.watch) { 
    initWatch(vm, options.watch)
  }
}
function initWatch(vm, watch) {
  for (let key in watch) {
    let handler = watch[key]
    // 这里不写数组的情况了,平时没用过
    createWatcher(vm, key, handler)
  }
}
  1. 执行createWatcher方法,拿到watch属性的具体回调函数,再执行vm.$watch方法
javascript 复制代码
function createWatcher(vm, expOrFn, handler, options) {
  // 对象
  if (handler.toString() === '[object Object]') {
    options = handler
    handler = handler.handler
  }
  // 监听的方法是methods里的方法
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}
  1. 执行$watch方法,给属性创建一个对应的watcher观察者实例,用来依赖收集。如果设置了immediate:true标识,表示立即执行一次回调函数
javascript 复制代码
Vue.prototype.$watch = function (expOrFn, cb, options) {
  options = options || {}
  // 用户自己设置的watch
  options.user = true 
  let watcher = new Watcher(this, expOrFn, cb, options)
  // 立即执行
  if (options.immediate) {
    pushTarget();
    cb.apply(this, [watcher.value])
    popTarget();
  }
}
  1. 创建watcher实例,进行依赖收集。如果watch的属性是定义在data中的,此watcher实例会被push进data中对应的属性的Dep对象的subs数组中。如果watch的属性是计算属性,此watcher实例会被push进计算属性中所有响应式数据的Dep对象的subs数组中
javascript 复制代码
class Watcher {
  constructor(vm, expOrFn, cb, options) {
    //...
    if (options) {
      this.user = !!options.user;
      this.deep = !!options.deep; // 是否深度监听
      this.lazy = !!options.lazy;
    }
    this.vm = vm;
    this.cb = cb; // 回调函数
    this.active = true;
    // 获取watch属性在vm实例上对应的值
    this.getter = parsePath(expOrFn); 
    this.value = this.lazy ? undefined : this.get();
  }
  get() {
    pushTarget(this);
    let vm = this.vm;
    let value = this.getter.call(vm, vm);
    // 深度监听
    if (this.deep) {
      traverse(value);
    }
    popTarget();
    return value;
  }
  update() {
  	// 进入异步队列,这个之前讲过,会触发this.run()
    queueWatcher(this);
  }
  run() {
    let value = this.get();
    if (value !== this.value || isObject(value) || this.deep) {
      let oldValue = this.value;
      this.value = value;
      if (this.user) {
        // 执行回调函数
        this.cb.apply(this.vm, [value, oldValue]);
      }
    }
  }
}

// 获取watch属性在vm实例上对应的值
// 可以是data中定义的属性,也可以是计算属性
function parsePath(path) {
  let segments = path.split(".");
  return function (obj) {
    if (!obj) return;
    for (let i = 0; i < segments.length; i++) {
      obj = obj[segments[i]];
    }
    return obj;
  };
}
function isObject(obj) {
  return obj !== null && typeof obj === "object";
}

// 递归处理触发每一个属性的getter,形成深度依赖收集
let seenObjects = new Set();
function traverse(val) {
  _traverse(val, seenObjects);
  seenObjects.clear(); // 清空
  return val;
}
function _traverse(val, seen) {
  let i;
  let keys;
  if (val.__ob__) {
    var depId = val.__ob__.dep.id;
    // 处理循环引用的情况
    if (seen.has(depId)) {
      return;
    }
    seen.add(depId);
  }
  if (Array.isArray(val)) {
    i = val.length;
    while (i--) _traverse(val[i], seen);
  } else {
    keys = Object.keys(val);
    i = keys.length;
    // 触发响应式数据的getter
    while (i--) _traverse(val[keys[i]], seen);
  }
}
  1. 当监听的响应式数据发生变化时,触发watcher.update()方法,最终执行watcher.run()方法,执行回调函数。

总结:watch与computed的区别

  1. watch无缓存功能,所监听的响应式属性值发生变化时,都会立即触发回调函数;
  2. watch更灵活,可以监听任何响应式数据或表达式的变更;
  3. watch支持深度监听,可支持在回调函数中执行异步操作,如网络请求等等。适用场景:数据变更后执行自定义逻辑,如网络请求、DOM操作。需要监听深层对象结构变化时。
  4. computed依赖其他数据属性进行计算,并返回计算结果,必须return出去一个值;
  5. computed具有缓存功能,计算结果进行缓存,提高性能。适用场景:所需的值是要根据多个响应式属性计算得出,并且会被频繁使用的值;
相关推荐
a栋栋栋2 小时前
apifox
java·前端·javascript
请叫我飞哥@2 小时前
HTML 标签页(Tabs)详细讲解
前端·html
Anlici3 小时前
React18与Vue3组件通信对比学习(详细!建议收藏!!🚀🚀)
前端·vue.js·react.js
m0_748251523 小时前
PDF在线预览实现:如何使用vue-pdf-embed实现前端PDF在线阅读
前端·vue.js·pdf
中生代技术3 小时前
3.从制定标准到持续监控:7个关键阶段提升App用户体验
大数据·运维·服务器·前端·ux
m0_748239333 小时前
从零开始:如何在.NET Core Web API中完美配置Swagger文档
前端·.netcore
m0_748232924 小时前
【前端】Node.js使用教程
前端·node.js·vim
hawleyHuo4 小时前
umi 能适配 taro组件?
前端·前端框架
web130933203984 小时前
[JAVA Web] 02_第二章 HTML&CSS
java·前端·html
黑客呀4 小时前
Go Web开发之Revel - 网页请求处理流程
开发语言·前端·web安全·golang·系统安全