「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 函数。
    • 可以监听多个参数,不能监听对象。
    • 只可以获取监听的数据的新值。
相关推荐
一个处女座的程序猿O(∩_∩)O1 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink4 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-6 小时前
验证码机制
前端·后端
燃先生._.7 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖8 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235248 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240259 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar9 小时前
纯前端实现更新检测
开发语言·前端·javascript