【深入watch】vue2 / 3的 watch()和$watch() 侦听使用与部分原理解释

616 阅读2分钟

$watch()、watch()的使用方式与功能 完全等效,在此案例只写composition api的watch()

本章涉及内容:概念、watch的使用方式、功能讲解。

  • 概念:
    • watch API 与选项式 API this.$watch (以及相应的 watch 选项) 完全等效。
    • watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用。
    • 缓冲回调:缓冲回调不仅可以提高性能,还有助于保证数据的一致性。在执行数据更新的代码完成之前,侦听器不会被触发。简单来说,同步修改数据时,修完操作执行完毕后才会触发回调。注意是同步!所以异步操作的时候,要注意多次触发watch的问题。(所有同步操作为1次,异步操作有几次就触发多少次监听!!)
    • 直接传入ref对象,会自动解包 .value

功能1:停止侦听
此处监听的ref对象,传入的是ref对象则会自动解包 .value(基于源码的解释)

<script lang='ts' setup>
import { watch,ref } from 'vue';
let count = ref(0)
//! 关于 watch() 返回的 StopHandle 函数,调用stop()将会停止侦听。
let stop = watch(count, (newValue, oldValue,InvalidateCbRegistrator) => {
  console.log('watch : count-:' + newValue, oldValue)
})
setTimeout(() => stop(), 3333); // stop() 停止侦听
</script>

功能2:监听 reactive() 的对象:
直接监听reactive声明的proxy对象,最终vue会默认赋值为true,所以自己传什么都没用。(基于源码的解释)

<script lang='ts' setup>
import { watch,reactive } from 'vue';
let proxy1 = reactive({})
watch(proxy1, (newValue, oldValue) => {
  console.log('proxy1--',newValue , oldValue )
},{
  deep:'干啥勒,对我做啥我都不从' //! 基于上述解释,此处传什么都是无效的,最终会被默认覆盖为true
})
</script>

功能3:监听嵌套对象的某个属性 (传入函数)
单独监听嵌套的某个属性 则需要传入函数的返回值

<script lang='ts' setup>
import { watch,reactive } from 'vue';
// reactive  ref都行,看了 refs 篇章你也知道其原理
let proxy1 = reactive({t1:'嵌套数据'}) 
watch(()=>proxy1.t1, (newValue, oldValue) => {
  console.log(newValue, oldValue)
})
</script>

功能4:监听多个源的形式 (传入数组)。

<script lang='ts' setup>
import { watch,reactive } from 'vue';
let data1 = reactive({t1:'t1嵌套数据'}) 
let data2 = reactive({t2:'t2嵌套数据'}) 
watch([data1,data2], (newValue, oldValue) => {
  console.log(newValue, oldValue)
})
</script>

  • 功能5:第三个参数的3种形式:
    1. flush 关于这个参数的讲解:首先 选项式api( watch:{} ) 、$watch() 、 watch()、watchEffect() 4个都能用!
    2. pre 模式下,指定的回调在模板渲染前被调用,所以立马获取对应的内容,将不会是最新的!!
    3. post 模式下,将回调推迟到模板渲染之后的,这时候用$ref获取对应内容则会是最新的,等于 pre 模式下用 nextTice()
    4. sync 就不多说了,回调改为同步调用,即取消 <缓冲回调> 这个功能
    5. 'pre' 和 'post' 回调使用队列进行缓冲,这也是为什么<同步>多次修改后,只触发最后一次监听回调的原因。
<script lang='ts' setup>
import { watch,reactive } from 'vue';
let data1 = reactive({t1:'t1嵌套数据'})
watch(data1, (newValue, oldValue) => {
  console.log('data1--',newValue, oldValue)
},{
  deep:false, // 用讲?不懂看选项API的watch
  immediate:false, // 用讲?不懂看选项API的watch
  flush:'post' // 'pre' | 'post' | 'sync'  // 默认值是'pre'
})

</script>

功能6:callback的第三个参数
解释看代码内的注释

<script lang='ts' setup>
import { watch,reactive } from 'vue';
let data1 = reactive({t1:'t1嵌套数据'})
let stop = watch(data1, (newValue, oldValue,InvalidateCbRegistrator) => {
  InvalidateCbRegistrator(()=>{
    // 在watch的 cb 执行之前,先会执行这个回调!。
    // 但是,第一次 watch 的cb执行后,该回调才正式开始生效,也就是说第一次是不执行的。

    // 当调用stop的时候,也会执行 InvalidateCbRegistrator ,最后的悲鸣~~
    console.log('执行了');
  })
})
data1.t1 = 1
settimeout(() => data1.t1 = 321, 1500)
setTimeout(() => stop(), 3333); // stop() 停止侦听
</script>

其他vue3 选项式api、组合式api、全局/应用api学习经验分享

oumae-kumiko.github.io/vue3.cn.com…