Vue3之watch和watchEffect实战总结

2023-11-11

watchwatchEffect都是vue3中的监听器,但是在写法和使用上是有区别的,主要是介绍一下watchwatchEffect的使用方法以及他们之间的区别。

watch 的工作原理:侦听特定的数据源,并在回调函数中执行副作用。它默认是惰性的——只有当被侦听的源发生变化时才执行回调,不过,可以通过配置 immediate 为 true 来指定初始时立即执行第一次。可以通过配置 deep 为 true,来指定深度监视

immdiate: 默认情况下,侦听器需要 data 后面值改变了才会生效,若需要侦听器一进入页面就生效,那就需要使用 immediate

deep: 默认情况下,侦听器只会监听数据本身的改变,若要进行深度监听,那就需要使用 deep。 immediate 和 deep 配置在第三个参数对象里

第一个参数:监听谁,第二个参数:回调函数,第三个参数:配置对象

watch监听单个数据

<template>
    <input type="text" v-model="text1" />
</template>

<script setup> 
    import { ref, watch } from 'vue'
    const text1 = ref('')

    watch(text1, (newVal, oldVal) => {console.log('监听单个数据', newVal, oldVal)
    }) 
</script> 

监听多个数据(初始值为空,并没有进行打印)

<template>
    <input type="text" v-model="text1" placeholder="text1值" />
    <hr /> 
    <input type="text" v-model="text2" placeholder="text2值" /> 
</template> 
<script setup>
import { ref, watch, reactive } from 'vue'
const text1 = ref('') 
const text2 = ref('')  
watch([text1,text2], (newValue, oldValue) => {
    console.log('监听一组数据变化', newValue, oldValue)
})

// { immediate: true }
</script> 

监听一个对象--问题

<template>
name: <input type="text" v-model="student.name" />
<hr/>
age: <input type="number" v-model="student.age" />
</template>

<script setup> import { reactive, watch } from 'vue'

const student = reactive({name: '',age: ''
})

watch(student, (newVal, oldVal) => {
console.log('newVal', newVal)
console.log('oldVal', newVal)
}) </script> 

监听对象的某一个值

<template>
    name: <input type="text" v-model="student.name" />
    <hr />
    age: <input type="number" v-model="student.age" />
</template> 
<script lang="ts" setup>
import { reactive, watch } from 'vue' 
const student = reactive({
    name: '', age: ''
}) 
watch(() => student.name, (newVal, oldVal) => {
    console.log('newVal', newVal)
    console.log('oldVal', newVal)
}, {
    deep: true, immediate: true
}) 

</script> 

遇到的坑:

1.监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)

<template>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <h2>薪资:{{ person.job.j1.salary }}K</h2>
    <button @click="person.name += '~'">修改姓名</button>
    <button @click="person.age++">增长年龄</button>
    <button @click="person.job.j1.salary++">涨薪</button>

    <hr />
    <input type="text" v-model="text1" />
</template>
   
<script>
import { reactive, watch, ref } from 'vue'
export default {
    setup() {
        //数据 
        let person = reactive({
            name: '张三',
            age: 18,
            job: {
                j1: {
                    salary: 20
                }
            }
        })
        const text1 = ref('')
        /* 情况三:监视reactive所定义的一个响应式数据的全部属性
                       1.注意:此处无法正确的获取oldValue
                       2.注意:强制开启了深度监视(deep配置无效)-不管嵌套有多深
           */
        watch(person, (newValue, oldValue) => {
            console.log('person变化了', newValue, oldValue)
        }, { deep: false }) //此处的deep配置无效 */

        watch(text1, (newVal, oldVal) => {
            console.log('监听单个数据', newVal, oldVal)
        })
        //返回一个对象(常用)
        return {
            person, text1
        }
    }
}
</script>

2.监视reactive定义的响应式数据中某个属性时:deep配置有效

<template>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <h2>薪资:{{ person.job.j1.salary }}K</h2>
    <button @click="person.name += '~'">修改姓名</button>
    <button @click="person.age++">增长年龄</button>
    <button @click="person.job.j1.salary++">涨薪</button> 
    <hr />
    <input type="text" v-model="text1" />
</template>
   
<script>
import { reactive, watch, ref } from 'vue'
export default {
    setup() {
        //数据 
        let person = reactive({
            name: '张三',
            age: 18,
            job: {
                j1: {
                    salary: 20
                }
            }
        })
        const text1 = ref('')
        /* 情况三:监视reactive所定义的一个响应式数据的全部属性
                       1.注意:此处无法正确的获取oldValue
                       2.注意:强制开启了深度监视(deep配置无效)-不管嵌套有多深
           */
        watch(person, (newValue, oldValue) => {
            console.log('person变化了', newValue, oldValue)
        }, { deep: false }) //此处的deep配置无效 */

        watch(text1, (newVal, oldVal) => {
            console.log('监听单个数据', newVal, oldVal)
        })
        watch(() => person.age, (newValue, oldValue) => {
            console.log('person的age变化了', newValue, oldValue)
        }, { deep: true }) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
        //返回一个对象(常用)
        return {
            person, text1
        }
    }
}
</script>

3.使用ref定义数组时:(触发changeArr的时候,监听不到)

<template>
    <button @click="changeArr">按钮</button>
</template>
   
<script>
import { reactive, watch, ref } from 'vue'
export default {
    setup() {
        const array = ref([1, 2, 3]);
        const changeArr = () => {
            console.log('触发');
            array.value = [];
        }
        watch(array.value, (now, old) => {
            console.log(now, old); // 触发changeArr的时候,监听不到
        })

        return {
            array, changeArr
        }
    }
}
</script>

解决方案:

<template>
    <button @click="changeArr">按钮</button>
</template>
   
<script>
import { reactive, watch, ref } from 'vue'
export default {
    setup() {
        const array = ref([1, 2, 3]);
        const changeArr = () => {
            console.log('触发');
            array.value = [];
        }
        // watch(array.value, (now, old) => {
        //     console.log(now, old); // 触发changeArr的时候,监听不到
        // })

        watch(() => [array.value], (now, old) => {
            console.log(now, old)
        })


        return {
            array, changeArr
        }
    }
}
</script>

watchEffect

watchEffect 函数的特点:

  • 优点:

    • 会自动收集依赖,不需要手动传递侦听内容——自动侦听回调函数中使用到的响应式数据。

    • 默认 immdiate 是 true,所以初始化时会立即执行。

  • 缺点:

    • 无法获得变化前的值(oldVal)。

watch() 是懒执行的:当数据源发生变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。

watchEffect相当于将watch 的依赖源和回调函数合并,当任何你有用到的响应式依赖更新时,该回调函数便会重新执行。不同于 watchwatchEffect 的回调函数会被立即执行(即 { immediate: true }

简单来说,watchEffect 是 Vue3 中的一个响应式 API,它允许你监听响应式状态的变化,并在其发生变化时触发副作用函数。这个特性非常有用,在我们需要对响应式数据进行操作的时候,我们可以在监听到变化后马上做出反应。

<template>
name: <input type="text" v-model="student.name" />
age: <input type="number" v-model="student.age" />
</template>

<script setup>

import { reactive, watchEffect } from 'vue'

const student = reactive({name: '',age: ''
})
watchEffect(() => {console.log('name: ',student.name, 'age: ', student.age)
}) 

</script> 

watcheffect停止监听

<template>
    <div>
        <input type="text" v-model="obj.name">
        <button @click="stopWatchEffect">停止监听</button>
    </div>
</template>
  
<script>
import { reactive, watchEffect } from 'vue';
export default {
    setup() {
        let obj = reactive({
            name: 'zs'
        });
        const stop = watchEffect(() => {
            console.log('name:', obj.name)
        })
        const stopWatchEffect = () => {
            console.log('停止监听')
            stop();
        }

        return {
            obj,
            stopWatchEffect,
        }
    }
}
</script> 

watchEffect的副作用

什么是副作用(side effect),简单的说副作用就是执行某种操作,如对外部可变数据或变量的修改,外部接口的调用等。watchEffect的回调函数就是一个副作用函数,因为我们使用watchEffect就是侦听到依赖的变化后执行某些操作。

Vue3watchEffect侦听副作用传入的函数可以接收一个 onInvalidate 函数作为入参,用来注册清理失效时的回调

当以下情况发生时,这个失效回调会被触发:

  • 副作用即将重新执行时(即依赖的值改变)

  • 侦听器被停止 (通过显示调用返回值停止侦听,或组件被卸载时隐式调用了停止侦听)

import { watchEffect, ref } from 'vue'

const count = ref(0)
watchEffect((onInvalidate) => {
  console.log(count.value)
  onInvalidate(() => {
    console.log('执行了onInvalidate')
  })
})

setTimeout(()=> {
  count.value++
}, 1000) 

上述代码打印的顺序为: 0 -> 执行了onInvalidate,最后执行 -> 1

分析:初始化时先打印count的值0, 然后由于定时器把count的值更新为1, 此时副作用即将重新执行,因此onInvalidate的回调函数会被触发,打印执行了onInvalidate,然后执行了副作用函数,打印count的值1

import { watchEffect, ref } from 'vue'

const count = ref(0)
const stop = watchEffect((onInvalidate) => {
  console.log(count.value)
  onInvalidate(() => {
    console.log('执行了onInvalidate')
  })
})

setTimeout(()=> {
  stop()
}, 1000)

上述代码:当我们显示执行stop函数停止侦听,此时也会触发onInvalidate的回调函数。同样,watchEffect所在的组件被卸载时会隐式调用stop函数停止侦听,故也能触发onInvalidate的回调函数。

【注意】:

watchEffect 会在 Vue3 开发中大量使用,这里说几个注意点:

  1. 如果有多个负效应,不要粘合在一起,建议写多个 watchEffect

watchEffect(() => {
  setTimeout(() => console.log(a.val + 1), 1000);
  setTimeout(() => console.log(b.val + 1), 1000);
});
//错误的

这两个 setTimeout 是两个不相关的效应,不需要同时监听 a 和 b,可以分开写

watchEffect(() => {
  setTimeout(() => console.log(a.val + 1), 1000);
});

watchEffect(() => {
  setTimeout(() => console.log(b.val + 1), 1000);
});

2.watchEffect 也可以放在其他生命周期函数内

onMounted(() => {
  watchEffect(() => {
    // access the DOM or template refs
  });
}

总结

watch

懒执行副作用——需要手动指明侦听的内容,也要指明侦听的回调。

默认 immdiate 是 false,所以初始化时不会执行,仅在侦听的源数据变更时才执行回调。

不需要有返回值。

可以获得变化前的值(oldVal)

watchEffect

自动收集依赖,不需要手动传递侦听内容——自动侦听回调函数中使用到的响应式数据

默认 immdiate 是 true,所以初始化时会立即执行,同时源数据变更时也会执行回调。

不需要有返回值。

无法获得变化前的值(oldVal)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue3之watch和watchEffect实战总结 的相关文章

随机推荐

  • idea工程在maven projects中显示灰色的解决办法

    在Mac上使用idea进行开发的过程中 一般在MavenProject中包含四个文件如下 1 profile 2 WebMavenTest 工程名 3 WebMavenTestSdk 工程名SDK 4 WebMavenTestService
  • 台式计算机显卡最高温度多少,台式机显卡温度多少是正常的(揭晓显卡正常温度度数)...

    PS 本文只讨论台式机 笔记本与台式机相比性能是偏低的 所以只要保证风扇能正常运转的话 基本上不会出现烧坏的情况 温度多少算正常 要知道电脑的硬件温度是不是过高 首先要了解硬件的正常温度范围 1 CPU 在电脑仅仅保持开机的情况下 一般是3
  • VLC打不开视频文件调试技巧

    用VLC打开TS文件 如果只有视频流的话可以打开 添加进了SRT字幕 打开失败 暴风 QQ影音 KMPlayer都可以正常打开 查询原因 下面是一个VLC自带的查询功能 或按快捷键Ctrl M 打开后的界面如下 注意下面的冗长等级是关键 它
  • 第1章 创建一个html网页

    创建一个html网页 目录标题 1 1认识html 1 2html标签 1 3html文件的基本结构 1 4Chrome的开发者工具 1 5在记事本中编写HTML文件 1 6使用编辑器创建HTML文档 1 6 1下载Hbuilder X 1
  • 位运算符详细解析

    位运算符计算 先把十进制转为二进制 计算完在转回十进制 以下位转换和计算规则 进制和 进制的转换 进制转 进制 标数除以2 若能除尽 该位记做0 若除不尽 该位记做1 再对商继续除以2 以 此类推 直到商为0 然后把每 位的结果反序组合就是
  • ROS语音更改API

    1 准备工作 申请科大讯飞帐号 下载SDK 打开 讯飞官网 创建语音合成需求 下载sdk 其中有libs库 并记录相应的appid 用于后续文件使用 下载的sdk中内容如下 我们将用到libs库中的文件 还需要更改 asr tts 两个文件
  • 【Linux 驱动篇(三)】新字符设备驱动

    文章目录 一 新字符设备驱动原理 1 分配和释放设备号 2 新的字符设备注册方法 2 1 字符设备结构 2 2 cdev init 函数 2 3 cdev add 函数 2 4 cdev del 函数 二 自动创建设备节点 1 mdev 机
  • 从0开始学习JavaScript--初识JavaScript

    一 JavaScript简介 1 JavaScript的起源 avaScript最初由Netscape的Brendan Eich设计 最初将其脚本语言命名为LiveScript 后来Netscape在与Sun合作之后将其改名为JavaScr
  • chatgpt网页版替代方法

    从昨天网上开始一直开着的chatgpt网页突然打不开了 提示1020错误 尝试换了不同代理软件或者代理地点仍然无法解决 也搜了很多资料 比如删除cookie 重启浏览器 更换浏览器等均不起作用 至今仍无法解决 具体错误内容如下 Access
  • 输入yum命令报错:Loaded plugins: fastestmirror You need to be root to perform this command.

    解决方法 是提示要获取root权限 输入su 回车输入密码即可
  • 计算机网络-子网划分(子网地址、广播地址、子网掩码)

    子网划分 题目 办公室内有一台计算机 IP地址为192 45 165 243 子网掩码为255 255 255 224 则该机所在的网络属于哪类网络 其网络是否进行了子网划分 若划分 则分为几个网络 并写出每个子网号 改机的子网号和广播地址
  • LFU算法族:window-LFU

    LFU算法族相关文章目录汇总 LFU算法 LFU Aging算法 window LFU算法 本文 1 LFU算法的不足 LFU Least Frequently Used 是一种缓存淘汰算法 LFU算法是根据缓存的访问频率 去淘汰访问次数最
  • JS程序

    注 题目来源 力扣 给定一个字符串 s 找到 s 中最长的回文子串 你可以假设 s 的最大长度为 1000 示例 1 输入 babad 输出 bab 注意 aba 也是一个有效答案 解题思路 这个题目是直接拍脑袋想法 就是暴力求解 思路是这
  • c++ cin整数以,(逗号)分割读取

    1 某些场景整数流不是空格分割 如用逗号分割 例如 下面的输入 要求每行是一个数组 一共两行测试输入 1 2 3 4 5 6 7 8 下面的代码就可以很好的解决问题 vector
  • C# 系统应用之注册表使用详解

    在平时做项目时 我们有时会遇到注册表的操作 例如前面我们需要获取IE浏览器地址栏的信息 获取 我的电脑 地址栏输入的文件夹信息 USB最近使用信息等 注册表项是注册表的基本组织单位 它包含子表项和值条目 简言之 注册表项相当于注册表里的文件
  • 闭包函数的理解

    function fn return function s console log hello return function s1 console log world var s fn console log s var s1 s con
  • windows上安装openSSH服务

    在windows上cmd 然后ssh 主机用户 主机ip直接连到远程 很方便 如图 那么怎么配置呢 首先windows上需要安装openSSH 1 下载openSSH windows版 注 该版本是64位 链接 https pan baid
  • java casting意思_Java Casting方法,不知道要强制转换为什么

    我今天在玩Java 发现有些奇怪 考虑以下代码 String foo cast hi int bar cast 1 cast 方法在这里 public static lt T gt T cast Object value return T
  • tkinter 动态显示时间的方法

    问题描述 有些小伙伴在使用python做GUI界面的时候可能想添加这么一个小功能 就是在界面的某个角落动态的显示当前的时间 本文将介绍具体方法 方式一 使用组件的after方法 代码如下所示 import time import tkint
  • Vue3之watch和watchEffect实战总结

    watch和watchEffect都是vue3中的监听器 但是在写法和使用上是有区别的 主要是介绍一下watch和watchEffect的使用方法以及他们之间的区别 watch 的工作原理 侦听特定的数据源 并在回调函数中执行副作用 它默认