# 1.watch 监视源只能是响应式对象

App 为根组件 下面 Home 例子都被此组件引用

vue
// App.vue 
<template>
  <div>
    <home></home>
  </div>
</template>
<script>
import Home from "./Home.vue";
export default {
  components: {
    Home,
  },
};
</script>
<style scoped>
</style>
vue
// Home.vue 
<template>
  <div>
    <h2>info:{{ info }}</h2>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>
<script>
import { watch } from "vue";
export default {
  setup() {
    const info = { name: "Neko", age: 16 }; //watch 传入不可是普通对象
    // 1. 侦听 watch
    watch(info, (newValue, oldValue) => {
      console.log("newValue:", newValue, "oldValue:", oldValue);
    });
    const changeAge = () => {
      info.age++;
    };
    return {
      info,
      changeAge,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

setup_watch

# 2. 传入一个 getter 函数

vue
// Home.vue
<template>
  <div>
    <h2>info:{{ info }}</h2>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>
<script>
import { reactive, watch } from "vue";
export default {
  setup() {
    const info = reactive({ name: "Neko", age: 16 });
    // 1. 侦听 watch 时,传入一个 getter 函数
    watch(
      () => info.age, // getter
      (newValue, oldValue) => {
        // 这里使用 ref 并没有触发 watch
        console.log("newValue:", newValue, "oldValue:", oldValue);
      }
    );
    const changeAge = () => {
      info.age++;
    };
    return {
      info,
      changeAge,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

setup_watch_getter

# 3. 侦听的 reactive 对象

vue
// Home.vue
<template>
  <div>
    <h2>info:{{ info }}</h2>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>
<script>
import { reactive, watch } from "vue";
export default {
  setup() {
    const info = reactive({ name: "Neko", age: 16 });
    // 1. 传入一个可响应式对象:reactive 对象 或 ref 对象
    //reactive 对象获取到的 newValue 和 oldValue 本身都是 reactive 对象
    watch(info, (newValue, oldValue) => {
      // 注意 newValue 和 oldValue 结果是不一样的
      console.log("newValue:", newValue, "oldValue:", oldValue); //newValue 和 oldValue 都是 Proxy
    });
    const changeAge = () => {
      info.age++;
    };
    return {
      info,
      changeAge,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

setup_watch_reactive

# 4. 侦听的 ref 对象

vue
// Home.vue
<template>
  <div>
    <h2>info:{{ info }}</h2>
    <button @click="changeInfo">修改年龄</button>
  </div>
</template>
<script>
import { ref, watch } from "vue";
export default {
  setup() {
    //const info = ref ({age: 16}); // 如果改变 age 不会触发 watch
    const info = ref("Neko");
    // 1. 传入一个可响应式对象:reactive 对象 或 ref 对象
    //ref 对象获取到的 newValue 和 oldValue 是 value 值的本身
    watch(info, (newValue, oldValue) => {
      // 这里使用 ref 并没有触发 watch
      console.log("newValue:", newValue); // 值都是 value ,不是 ref 对象
      console.log("oldValue:", oldValue);
    });
    const changeInfo = () => {
      //info.value.age++; // 不会触发 watch
      info.value = "注意如果info是一个对象,则改变里面的数据不会触发 watch";
    };
    return {
      info,
      changeInfo,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

setup_watch_ref

# 5. 使用 getter 对 reactive 对象为普通对象

vue
// Home.vue
<template>
  <div>
    <h2>info:{{ info }}</h2>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>
<script>
import { reactive, watch } from "vue";
export default {
  setup() {
    const info = reactive({ name: "Neko", age: 16 });
    // 1. 传入一个可响应式对象:reactive 对象 或 ref 对象
    //reactive 对象获取到的 newValue 和 oldValue 本身都是 reactive 对象
    // 如果希望 newValue 和 oldValue 是一个普通的对象
    watch(
      // 相当于变成了一个 getter, 返回了一个对象
      () => ({ ...info }),
      (newValue, oldValue) => {
        // 注意 newValue 和 oldValue 结果这次是不一样的
        console.log("newValue:", newValue, "oldValue:", oldValue); // 此时已经是普通的对象了
      }
    );
    const changeAge = () => {
      info.age++;
    };
    return {
      info,
      changeAge,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

setup_watch_reactive_getter

# 6. 关于使用 getter 解构后不是深度监听

vue
// Home.vue
<template>
  <div>
    <h2>info:{{ info }}</h2>
    <button @click="changeInfo">修改年龄</button>
  </div>
</template>
<script>
import { reactive, watch } from "vue";
export default {
  setup() {
    const info = reactive({
      name: "Neko",
      age: 16,
      friend: {
        name: "Nico",
      },
    });
    // 1. 侦听 watch 时,传入一个 getter 函数
    watch(
      // 使用 getter 解构后不是深度监听
      () => ({ ...info }),
      (newValue, oldValue) => {
        console.log("newValue:", newValue, "oldValue:", oldValue);
      }
    );
    const changeInfo = () => {
      info.friend.name = "Aphrodite"; // 无法深度侦听
    };
    return {
      info,
      changeInfo,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

watch_reactive_not_deep

# 7.watch 第三个参数 (对象) 中的 deep 属性

vue
// Home.vue
<template>
  <div>
    <h2>info:{{ info }}</h2>
    <button @click="changeInfo">修改年龄</button>
  </div>
</template>
<script>
import { reactive, watch } from "vue";
export default {
  setup() {
    const info = reactive({
      name: "Neko",
      age: 16,
      friend: {
        name: "Nico",
      },
    });
    // 1. 侦听 watch 时,传入一个 getter 函数
    watch(
      // 使用 getter 解构后不是深度监听
      () => ({ ...info }),
      (newValue, oldValue) => {
        console.log("newValue:", newValue, "oldValue:", oldValue);
      },
      {
        deep: true, // 使用 deep 深度侦听
      }
    );
    const changeInfo = () => {
      info.friend.name = "Aphrodite";
    };
    return {
      info,
      changeInfo,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

watch_reactive_deep

# 8.watch 第三个参数 (对象) 中 的 immediate 属性

vue
// Home.vue
<template>
  <div>
    <h2>info:{{ info }}</h2>
    <button @click="changeInfo">修改年龄</button>
  </div>
</template>
<script>
import { reactive, watch } from "vue";
export default {
  setup() {
    const info = reactive({
      name: "Neko",
      age: 16,
      friend: {
        name: "Nico",
      },
    });
    // 1. 侦听 watch 时,传入一个 getter 函数
    watch(
      // 使用 getter 解构后不是深度监听
      () => ({ ...info }),
      (newValue, oldValue) => {
        console.log("newValue:", newValue, "oldValue:", oldValue);
      },
      {
        deep: true, // 使用 deep 深度侦听
        immediate: true, // 立即执行,第一次也会打印。 不过因为未发生改变,所以没有 oldValue 值
      }
    );
    const changeInfo = () => {
      info.friend.name = "Aphrodite";
    };
    return {
      info,
      changeInfo,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

watch_reactive_immediate

# 9. 侦听多个数据源(数组)

vue
// Home.vue
<template>
  <div>
    <h2 ref="title">[info.name]:{{ info.name }}</h2>
    <button @click="changeData">修改数据</button>
  </div>
</template>
<script>
import { ref, reactive, watch } from "vue";
export default {
  setup() {
    // 1. 定义可响应式的对象
    const info = reactive({ name: "Neko", age: 18 });
    const name = ref("aimer");
    console.log("info:", info);
    console.log("name:", name);
    // 2. 侦听器 watch 使用数组侦听多个数据源
    watch(
      // 解构 reactive 对象
      [() => ({ ...info }), name],
      ([newInfo, newName], [oldInfo, oldName]) => {
        console.log(newInfo, newName, oldInfo, oldName);
      }
    );
    const changeData = () => {
      info.name = "Nico";
    };
    return {
      changeData,
      info,
    };
  },
};
</script>
<style scoped>
</style>

参考结果:

setup_watch_param_array

# 10. 总结

  • watch 侦听函数的数据源有两种类型
    • 一个 getter 函数:但是该 getter 函数必须引用可响应式的对象 (比如 reactive 或者 ref)
    • 直接写入一个可响应式的对象,reactive 或者 ref (比较常用的是 ref)
  • watch 侦听 reactive
    • watch 侦听的 reactive 获取到的 newValue 和 oldValue 的引用地址是一样的
    • watch 侦听获取的 newValue 和 oldValue 本身都是 reactive 对象
    • 而对 reactive 对象 使用 getter 函数,是可以获取到与 ref 一样的普通对象
  • watch 侦听 ref
    • watch 侦听 ref 如果直接赋值是可以侦听到的 (比如:info.value = ???)
    • watch 侦听 ref 的 newValue 和 oldValue 是能获取到的 (就是引用地址不一样)
    • watch 侦听的 ref,如果是一个对象,并且改变的是对象里面的属性,是侦听不到的 (比如:info.value.age++)
  • watch 侦听 (reactive/ref) 对象 并传入 getter 函数, newValue 和 oldValue 可以获取到不同值
  • watch 与 options API 的 watch 使用基本一致(deep/immediate)
  • watch 侦听多个数据源可以使用(数组)方式