Vue中watch使用

18,766 阅读1分钟

虽然Vue.js为我们提供了有用的computed, 但在某些场景下, 仍然还是需要使用到watch.

默认情况下, watch只在被监听的属性值发生变化时执行.

例如:

export default {
  data: () => ({
    dog: ""
  }),
  watch: {
    dog(newVal, oldVal) {
      console.log(`Dog changed: ${newVal}`);
    }
  }
};

如上代码所示, 只有当dog的值有发生改变时, watch中的dog函数才会执行.

watch options

immediate

但是, 在某些情况下, 你可能需要在创建组件后立即运行监听程序. 当然, 你可以将逻辑迁移至methods中, 然后从watchcreated钩子函数中分别调用它, 但有没有更简单一点的办法呢?

你可以在使用watch时, 使用immediate: true选项, 这样它就会在组件创建时立即执行.

export default {
  data: () => ({
    dog: ""
  }),
  watch: {
    dog: {
      handler(newVal, oldVal) {
        console.log(`Dog changed: ${newVal}`);
      },
      immediate: true
    }
  }
};

deep

watch中还有一个属性: deep, 默认值为: false, 即是否需要开启深度监听. 例如:

export default {
  data: () => ({
    obj: {
      hello: 'james'
    }
  }),
  watch: {
    obj: {
      handler(newVal, oldVal) {
        console.log(`obj changed: ${newVal}`);
      },
      immediate: true,
      deep: true
    }
  }
};

deep即深入观察, 监听器会层层遍历, 给对象的所有属性(及子属性)添加监听器. 这样做无疑会有很大的性能开销, 修改obj中任何一个属性都会触发监听器中的处理函数.

若只想监听obj中的某个属性, 可以使用字符串监听形式.

export default {
  data: () => ({
    obj: {
      hello: 'james'
    }
  }),
  watch: {
    'obj.hello': {
      handler(newVal, oldVal) {
        console.log(`obj changed: ${newVal}`);
      },
      immediate: true,
      deep: false
    }
  }
};

动态添加watch

Vue源代码中$watch的实现:

Vue.prototype.$watch = function (
  expOrFn,
  cb,
  options
) {
  var vm = this;
  if (isPlainObject(cb)) {
    return createWatcher(vm, expOrFn, cb, options)
  }
  options = options || {};
  options.user = true;
  var watcher = new Watcher(vm, expOrFn, cb, options);
  if (options.immediate) {
    try {
      cb.call(vm, watcher.value);
    } catch (error) {
      handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
    }
  }
  return function unwatchFn () {
    watcher.teardown();
  }
};

基于此, 我们可以将上面的示例代码修改为 动态添加watch, 例如:

export default {
  data: () => ({
    obj: {
      hello: 'james'
    }
  }),
  mounted(){
    this.$watch('obj.hello', this.handler, {
      immediate: true,
      deep: false
    })
  },
  methods: {
    handler(newVal, oldVal) {
      console.log(`obj changed: ${newVal}`);
    }
  }
};

Warning:
正常情况下, 不推荐使用$watch来动态添加watch, 因为你还需要手动注销watch监听事件

注销watch

若使用动态添加watch, 就需要手动注销了. 从源代码中, 可以看出: this.$watch调用后会有一个返回值, 通过调用此返回值, 即可注销watch.

修改代码如下:

let unWatch = null
export default {
  data: () => ({
    obj: {
      hello: 'james'
    }
  }),
  mounted(){
    unWatch = this.$watch('obj.hello', this.handler, {
      immediate: true,
      deep: false
    })
  },
  methods: {
    handler(newVal, oldVal) {
      console.log(`obj changed: ${newVal}`);
    }
  },
  beforeMount(){
    unWatch()
    unWatch = null
  }
};