「Vue3学习篇」-watchEffect()、watchPostEffect()、watchSyncEffect()

『引言』

在日常开发中,慢慢地就会发现遇到的需求总是多变的,如果要实现下面这个需求会怎么做呢?

🙋🙋‍♂️提问 🚩:要实现第一次渲染页面的时候,watch里的回调函数就执行一遍,怎么做❓🤔🤔

回答 📒:使用watch实现,就是把回调函数直接提取出来,进入页面执行一遍,这样操作比较繁琐。其它方法的话,可以使用今日的主角watchEffect

watchEffect相当于将 watch 的依赖源和回调函数合并,它会立即执行传入的一个函数,自动收集其依赖的函数,监视响应式数据的变化,每次变化都会触发回调函数的重新执行。

watchEffect 监听是立即执行的,会默认初始时就会执行第一次, 从而可以收集需要监听的数据。

『watchEffect()』

『定义』

【官方解释】立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

  • 详细信息

    第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。

    第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。

    默认情况下,侦听器将在组件渲染之前执行。设置 flush: 'post' 将会使侦听器延迟到组件渲染之后再执行。详见回调的触发时机。在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置 flush: 'sync' 来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。

    返回值是一个用来停止该副作用的函数。

【我的理解】 watchEffect 可以看成是 watchside effct,那么就可以理解成,在观察『watch』到变化后执行一些操作『side effct』。

『用法』

watchEffect(effect,options)

『watchEffect参数说明』

  • 第一个参数:(必传)effect 函数,收集依赖,在组件初始化时立即执行一次。

    并且 effect 函数可以接受一个 onInvalidate 函数参数,该参数执行并传入一个 callback ,每次监听回调执行前都会执行该 callback。

  • 第二个参数对象(非必传): flush 、 onTrack 、 onTrigger。『onTrack 、 onTrigger』只能在开发模式下工作。

    • flush: 跟 watch() 的flush 完全一致,『 'pre' | 'post' | 'sync' 』默认:'pre'。
    • onTrack:跟踪之前会触发该函数,收集了多少个依赖就触发多少次,返回对应依赖信息。
    • onTrigger:依赖更改就触发执行,同步的,没有缓冲回调。

官网示例🌰

『注意』

  • watchEffect不会返回一个停止副作用的函数。如果需要停止副作用函数的执行,需要使用watch API,并通过返回的停止函数来实现。
  • 使用时不需要具体指定监听的谁,回调函数内直接使用就可以。
  • 只能访问当前最新的值,访问不到修改之前的值。

『示例🌰:watchEffect 监听数据』

xml 复制代码
<template>
  <div>
    <h1>人物简介</h1>
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}岁</p>
    <p>爱好:{{info.hobbies.join('、')}}</p>
    <p>地址:{{info.address.provice}} - {{info.address.city}}</p>
    <p>描述:{{info.description}}</p>
    <button @click="modifyInfo">
      修改数据
    </button>
  </div> 
</template>

<script setup>
import { ref, reactive, watchEffect } from 'vue'
    
   const name = ref('pupu') 
   const age = ref(10)
   const info = reactive({
      hobbies: ['唱歌','画画'],
      address: {
          provice: '浙江省',
          city: '杭州市'
      },
      description: '一点也不可爱,不喜欢吃蜂蜜!',
    })
    
    const modifyInfo = () => {
        name.value = 'wnxx'
        age.value = 3 
        info.hobbies = ['打羽毛球','旅游']
        info.address.provice = '云南省'
        info.address.city = '丽江市'
        info.description = '非常的可爱,特别喜欢吃蜂蜜!'   
    } 
    // 情况1: 监听ref定义的数据
    watchEffect(() => {
      console.log(name.value, 'name')
    })
    // 情况2: 监听reactive定义的数据
    watchEffect(() => {
      console.log(info.address.provice, info.address.city, 'address')
    })
    // 情况3: 监听reactive定义的对象数据
    watchEffect(() => {
      console.log(info, 'info对象')
    })
    // 情况4: watchEffect什么时候执行
    watchEffect(() => {
      console.log("执行了")
    })
</script>

『效果展示』

『代码解析』

上述示例代码中,分别介绍了几种情况。

  • 情况1: 监听ref定义的数据,这种情况下监听的是『name属性』,在回调函数中打印了一下『name』的值,可以在控制台中看到,第二次点击修改按钮,『watchEffect』触发了。

  • 情况2: 监听reactive定义的数据,这时监听的是『info.address.provice和info.address.city』,同样的在回调函数中打印一下,可以在控制台中看到,第二次点击修改按钮,『watchEffect』也触发了。

  • 情况3: 监听reactive定义的对象数据,这时监听的是『info对象』,也在回调函数中打印了一下,可以看到第一次立即执行时执行了,第二次点击修改按钮后,改变『info』『watchEffect』没触发。

    这是因为watchEffect只会跟踪回调函数中使用的属性,不会递归跟踪对象中所有的属性。

  • 情况4: watchEffect什么时候执行,这种情况可以看到第一次立即执行时执行了,第二次并没有执行,控制台没有打印任何信息。

    这是因为『watchEffect』使用时不需要具体指定监听的谁,回调函数内直接使用就可以,那也就是回调函数中使用了就执行,没使用就不会执行。

『示例🌰:watchEffect的副作用』

xml 复制代码
<template>
  <div>
    <h1>人物简介</h1>
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}岁</p>
    <p>爱好:{{info.hobbies.join('、')}}</p>
    <p>地址:{{info.address.provice}} - {{info.address.city}}</p>
    <p>描述:{{info.description}}</p>
    <button @click="modifyInfo">
      修改数据
    </button>
  </div> 
</template>

<script setup>
import { ref, reactive, watchEffect } from 'vue'
    
   const name = ref('pupu') 
   const age = ref(10)
   const info = reactive({
      hobbies: ['唱歌','画画'],
      address: {
          provice: '浙江省',
          city: '杭州市'
      },
      description: '一点也不可爱,不喜欢吃蜂蜜!',
    })
    
    const modifyInfo = () => {
        name.value = 'wnxx'
        age.value = 3 
        info.hobbies = ['打羽毛球','旅游']
        info.address.provice = '云南省'
        info.address.city = '丽江市'
        info.description = '非常的可爱,特别喜欢吃蜂蜜!'   
    } 
    
    watchEffect(
      (onInvalidate) => {
        console.log(name.value, 'name')
        onInvalidate(()=> {
          console.log('执行了onInvalidate')
        })
      })
</script>

『效果展示』

『代码解析』

因为watchEffect就是侦听到依赖的变化后执行某些操作,所以watchEffect的回调函数就是一个副作用函数。

watchEffect侦听副作用传入的函数可以接收一个 onInvalidate 函数作为入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

  • 副作用即将重新执行时(即依赖的值改变)
  • 侦听器被停止 (通过显示调用返回值停止侦听,或组件被卸载时隐式调用了停止侦听)

在上述代码中,定义了一些数据、点击按钮修改信息、对『name』数据进行监听。在对『name』数据进行监听时,初始化时会在控制台打印『name』的值为『pupu』,由于点击按钮会修改『name』的值,对『name』的值进行了更新,所以副作用就会重新执行,此时onInvalidate的回调函数会被触发,控制台就打印了执行了onInvalidate,之后会执行副作用函数,打印『name』的值为『wnxx』。

『示例🌰:关闭 watchEffect 监听』

xml 复制代码
<template>
  <div>
    <h1>人物简介</h1>
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}岁</p>
    <p>爱好:{{info.hobbies.join('、')}}</p>
    <p>地址:{{info.address.provice}} - {{info.address.city}}</p>
    <p>描述:{{info.description}}</p>
  </div> 
</template>

<script setup>
import { ref, reactive, watchEffect } from 'vue'
    
   const name = ref('pupu') 
   const age = ref(10)
   const info = reactive({
      hobbies: ['唱歌','画画'],
      address: {
          provice: '浙江省',
          city: '杭州市'
      },
      description: '一点也不可爱,不喜欢吃蜂蜜!',
    })
    
    const modifyInfo = () => {
        name.value = 'wnxx'
        age.value = 3 
        info.hobbies = ['打羽毛球','旅游']
        info.address.provice = '云南省'
        info.address.city = '丽江市'
        info.description = '非常的可爱,特别喜欢吃蜂蜜!'   
    } 
    
   const unwatchEffect = watchEffect((onInvalidate) => {
        console.log(name.value, 'name')
        onInvalidate(()=> {
          console.log('执行了onInvalidate')
        })
    })

    setTimeout(()=> {
      modifyInfo()
      unwatchEffect()
    }, 1000)
</script>

『效果展示』

『代码解析』

关闭watchEffect 监听,其实非常简单,上述代码中创建一个 watchEffect 监听器,只需要将调用一下就关了。

还有就是在上述代码中,关闭watchEffect 监听,执行unwatchEffect函数停止监听,就会触发onInvalidate的回调函数。

『watchPostEffect()』

『定义』

【官方解释】watchEffect() 使用 flush: 'post' 选项时的别名。

『注意』

现在有这样一个疑问,也是通过一个示例进行解答,到底是什么疑问,一起看看下面👇。

提问🚩:在监听器的回调函数中获取DOM,这个时候的DOM是更新前的还是更新后的❓🤔🤔

『示例🌰』

xml 复制代码
<template>
  <div>
    <h1>人物简介</h1>
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}岁</p>
    <p>爱好:{{info.hobbies.join('、')}}</p>
    <p>地址:{{info.address.provice}} - {{info.address.city}}</p>
    <p>描述:{{info.description}}</p>
    <button @click="modifyInfo">
      修改数据
    </button>
  </div> 
</template>

<script setup>
import { ref, reactive, watchEffect, watchPostEffect } from 'vue'
    
   const name = ref() 
   const age = ref(10)
   const info = reactive({
      hobbies: ['唱歌','画画'],
      address: {
          provice: '浙江省',
          city: '杭州市'
      },
      description: '一点也不可爱,不喜欢吃蜂蜜!',
    })
    
    const modifyInfo = () => {
        name.value = 'wnxx'
        age.value = 3 
        info.hobbies = ['打羽毛球','旅游']
        info.address.provice = '云南省'
        info.address.city = '丽江市'
        info.description = '非常的可爱,特别喜欢吃蜂蜜!'   
    } 
    // 情况1
    watchEffect(
      () => {
        console.log(name.value, 'name')
      })
    // 解决办法1
    watchEffect(
      ()=>{ 
        console.log(name.value, '更新后的DOM节点name1')
      },
      {
       flush: 'post'
      })
    // 解决办法2
    watchPostEffect(() => {
      console.log(name.value, '更新后的DOM节点name2')
    })
</script>
 

『效果展示』

『代码解析』

上述代码中,监听的是『name』的值,当监听『name』的值改变的时候,『vue组件会更新、监听器会回调』

一般监听器的回调会比vue组件的更新先一步调用。所以在监听器回调中,访问的DOM节点是未更新的。

当想要在监听器回调中访问更新后的DOM节点时,可以传递一个flush: 'post'参数,或者使用更简便的api watchPostEffect()

『watchSyncEffect()』

『定义』

【官方解释】 watchEffect() 使用 flush: 'sync' 选项时的别名。

在组件更新之前或更新之前,立即同步调用它。通常,它在onBeforeUpdate之前调用。

『watch 与 watchEffect 的区别』

不同点:

  • 『watch』
    • 页面第一次加载时不触发watch函数。
    • 可以以数组的形式监听多个参数,如果多个数据同时发生改变,watch只触发一次。
    • 可以获取监听的数据的新值和旧值。
  • 『watchEffect』
    • 页面第一次加载时就会触发 watchEffect 函数。
    • 可以监听多个参数,不能监听对象。
    • 只可以获取监听的数据的新值。
相关推荐
又尔D.2 小时前
vue3+webOffice合集
vue.js·weboffice
古蓬莱掌管玉米的神5 小时前
vue3语法watch与watchEffect
前端·javascript
林涧泣5 小时前
【Uniapp-Vue3】uni-icons的安装和使用
前端·vue.js·uni-app
雾恋5 小时前
AI导航工具我开源了利用node爬取了几百条数据
前端·开源·github
拉一次撑死狗5 小时前
Vue基础(2)
前端·javascript·vue.js
祯民6 小时前
两年工作之余,我在清华大学出版社出版了一本 AI 应用书籍
前端·aigc
热情仔6 小时前
mock可视化&生成前端代码
前端
m0_748246356 小时前
SpringBoot返回文件让前端下载的几种方式
前端·spring boot·后端
wjs04066 小时前
用css实现一个类似于elementUI中Loading组件有缺口的加载圆环
前端·css·elementui·css实现loading圆环
爱趣五科技6 小时前
无界云剪音频教程:提升视频质感
前端·音视频