惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Exploit Database - CXSecurity.com
A
Arctic Wolf
K
Kaspersky official blog
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy International News Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
U
Unit 42
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
P
Privacy & Cybersecurity Law Blog
O
OpenAI News
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Cisco Blogs
AWS News Blog
AWS News Blog
Vercel News
Vercel News
Microsoft Security Blog
Microsoft Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
美团技术团队
T
Threatpost
S
Schneier on Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
Cyber Attacks, Cyber Crime and Cyber Security
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
Blog — PlanetScale
Blog — PlanetScale
C
Cybersecurity and Infrastructure Security Agency CISA
F
Full Disclosure
博客园_首页
N
Netflix TechBlog - Medium
Security Latest
Security Latest
有赞技术团队
有赞技术团队
Google DeepMind News
Google DeepMind News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Register - Security
The Register - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Recent Announcements
Recent Announcements
博客园 - Franky
P
Palo Alto Networks Blog
Project Zero
Project Zero
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
H
Help Net Security
Hacker News: Ask HN
Hacker News: Ask HN
Cisco Talos Blog
Cisco Talos Blog
H
Heimdal Security Blog
The Hacker News
The Hacker News
博客园 - 【当耐特】
GbyAI
GbyAI

博客园 - springsnow

npoi读取word 内容控件 如何使用 Vue SFC Playground toRefs学习 Vue3中如何响应式解构 props useTemplateRef使用 以后台方式启动RealVNC 在VS2022和VS2012共存的电脑上安装VS212扩展注意事项 VS中配置AnkhSVN源代码比较文件排列方式 使用VSCode撰写和发布博客园文章 DBever导入越南文Excel 复制对象中的一部分属性给另一个对象(对象部分属性解构到新对象) 使用metaWebBlog接口实现博客文章同步 .Net Core3.1上用EFCore的反向工程生成 水淼·文件批量处理器 如何高效的在博客园上编写MD格式的博客(插件pycnblog,推荐) 自动备份软件 —— Syncovery 7.98s Pro、Enterprise VMware 第三方百度网盘客户端 PanDownload、速盘、panlight 本地电脑视频播放器推荐PotPlayer、KMPlayer
Vue3中 watch、watchEffect 详解
springsnow · 2024-09-29 · via 博客园 - springsnow

点击查看官方文档

1. watch 的使用

语法

import { watch } from "vue"
watch( name , ( curVal , preVal )=>{ //业务处理 }, options ) ;

共有三个参数,分别为:
name:需要帧听的属性;
(curVal,preVal)=>{ //业务处理 } 箭头函数,是监听到的最新值和本次修改之前的值,此处进行逻辑处理。
options :配置项,对监听器的配置,如:是否深度监听。

watch() 默认是 懒侦听的,即仅在 侦听源发生变化时才执行回调函数。

1.1 监听 ref 定义的响应式数据

1、监听 ref() 定义的【基本类型】数据:watch 参数一 直接写数据名即可,监听的是其 value值的改变。

<template>
  <div>
    <div>值:{{ count }}</div>
    <button @click="add">改变值</button>
  </div>
</template>

<script lang="ts" setup>
import { ref, watch } from 'vue';

const count = ref(0);

const add = () => {
  count.value++
};

watch(count, (newVal, oldVal) => {
  console.log('值改变了', newVal, oldVal)
})

</script>

2、监视 ref()定义的【引用类型】数据:直接写数据名,监视的是 源数据的【地址值(堆 / 栈 内存的关系)】,若想深层监视对象内部的数据,则需要 手动开启深度监视

依次点击下方三个事件的时候,会出现一个问题,那就是只有在触发 整体被修改 事件,整体更改 numdata.value 属性值的时候, watch 数据监听才会触发,而如果只更改了 numdata 数据中的 某一个属性值watch 数据监听不会触发

<template>
    <h1>{{ numdata.name }}</h1>
    <h1>{{ numdata.age }}</h1>

    <button @click="shownum">修改名</button>
    <button @click="shownum2">修改年龄</button>
    <button @click="shownum3">修改全部</button>
</template>

<script setup lang="ts" name="Box">
    import { ref, watch } from 'vue';


    type typeData = {
        name: string,
        age: number
    };

    let numdata = ref<typeData>({
        name: "张三",
        age: 1
    });

    let describe = ref<string>("");
  
    const shownum = (): void => {
        numdata.value.name = "李四";
        describe.value = "名字被修改"
    };

    const shownum2 = (): void => {
        numdata.value.age = 456;
        describe.value = "年龄被修改"

    };

    const shownum3 = (): void => {
        numdata.value = {
            name: "王五",
            age: 789
        }
        describe.value = "整体被修改"
    };

    watch(numdata, (newValue, OldValue): void => {
        console.log("数据变化了",describe.value, newValue, OldValue);
    });

</script>

针对上面的问题,我们需要在 watch 的第三个参数配置对象中 对监听的数据 手动开启深度监视{ deep: true }

数组同理:如果直接通过 数组下标索引 修改值,默认也是监听不到的,也需要 开启 { deep: true }

    watch(numdata, (newValue, OldValue): void => {
        console.log("数据变化了",describe.value, newValue, OldValue);
    }, { deep: true });

3、在上面的情况中,还有一个问题值得注意:那就是当我们,在修改数据中某一个属性的时候 会出现 newValue 和 oldValue 都是同一个新值的情况,因为它们是指向同一个源对象地址(数组同理:如果直接通过 数组下标索引 修改某个值则 newValue 和 oldValue 也会是同一个新值的情况)。

若修改了整个 ref.value的值 , newValue就则是新的值, oldValue则是旧的值,这是因为相当于从新分配了一块内存空间,新旧数据已经不指向同一块源对象地址了。

1.2监听 reactive 定义的响应式数据

1、监视 reactive() 定义的响应式数据时,且 默认自动开启了深度监视,并且该深度监视 不可通过配置项 { deep: false } 关闭

介于 reactive 的设计原理,在修改 reactive 定义的数据时,无法整体修改其变量名数据,这会使其丢失响应式,故而,在操作 reactive 定义的数据时,需保持只通过 属性 key 或者 下标索引值去修改某个数据,尽量避免直接修改 reactive 数据源本体。

<template>
  <div>
    <div>{{ obj.name }}</div>
    <div>{{ obj.age }}</div>
    <button @click="changeName">改变值</button>
    <button @click="changeAll ">修改全部值</button>
  </div>
</template>

<script lang="ts" setup>
import { reactive, watch } from 'vue';

const obj = reactive({
  name: 'zs',
  age: 14
});

const changeName = () => {
  obj.name = 'ls';
};

const changeAll = (): void => {
    console.log("修改全部属性");
    Object.assign(obj, { name: "王五", age: 789 })  //
};


watch(obj, (newVal, oldVal) =>
  {
    console.log('值改变了', newVal, oldVal)
  }
)

</script>

2、注意:当只修改嵌套的属性触发监听时 newValue 此处和 oldValue 是相等的 因为它们是同一个对象!

<template>
    <h1>{{ numdata[0] }}</h1>
    <h1>{{ numdata[1] }}</h1>

    <button @click="shownum">修改下标0</button>
    <button @click="shownum2">修改下标1</button>
</template>

<script setup lang="ts" name="Box">
import { reactive, watch } from 'vue';


let numdata = reactive<number[]>([1, 2]);

const shownum = (): void => {
    numdata[0] = 11
    console.log("下标0 被修改");
};

const shownum2 = (): void => {
    numdata[1] = 22
    console.log("下标 1 被修改");
};



watch(numdata, (newValue, OldValue): void => {
    console.log("数据变化了", newValue, OldValue);

    // 注意:当只修改嵌套的属性触发监听时 `newValue` 此处和 `oldValue` 是相等的
    // 因为它们是同一个对象!
}, { deep: false });     //   { deep: false }   默认开启深度监视,且无法通过配置项关闭。

</script>

1.3 监听多个响应式数据数据

一次性监听多个 多个数据 写法格式为一个 数组

<template>
    <h1>{{ data1 }}</h1>
    <h1>{{ data2.name }}</h1>

    <button @click="shownum">修改data1</button>
    <button @click="shownum2">修改data2</button>
</template>

<script setup lang="ts" name="Box">
    import { ref, watch, reactive } from 'vue';


    let data1 = ref<number>(1);

    let data2 = reactive({
        name: "张三"
    });

    const shownum = (): void => {
        data1.value += 1
    };

    const shownum2 = (): void => {
        data2.name = "李四"
    };


    // 同时监听 data1 和 data2.name 等多个数据的变化  写法格式为一个数组  ,回调函数接收两个数组,分别对应来源数组中的新值和旧值:
    watch([data1, () => data2.name], ([newValuedata1, newValuedata2], [OldValuedata1, OldValuedata2]): void => {
        console.log("数据变化了新值", newValuedata1, newValuedata2);
        console.log("数据变化了旧值", OldValuedata1, OldValuedata2);
    });
</script>

1.4 监听对象中某个属性的变化

能直接侦听响应式对象的属性值,需要用一个返回该属性的 getter 函数:****

当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。

如果你想让回调在深层级变更时也能触发,你需要使用 {deep:true } 强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。

<template>
  <div>
    <div>{{ obj.name }}</div>
    <div>{{ obj.age }}</div>
    <button @click="changeName">改变值</button>
  </div>
</template>

<script lang="ts" setup>
import { reactive, watch } from 'vue';

const obj = reactive({
  name: 'zs',
  age: 14
});

const changeName = () => {
  obj.name = 'ls';
};

watch(() => obj.name, () => {
  console.log('监听的obj.name改变了')
})

</script>

1.5 默认执行(immediate)

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

举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。选项来强制侦听器的回调立即执行:我们可以通过传入 immediate:true

<template>
  <div>
    <div>{{obj.brand.name}}</div>
    <button @click="changeBrandName">改变值</button>
  </div>
</template>

<script lang="ts" setup>
  import { reactive, ref, watch } from 'vue';

  const obj = reactive({
    name:'zs',
    age:14,
    brand:{
      id:1,
      name:'宝马'
    }
  });

  const changeBrandName = () => {
    obj.brand.name = '奔驰1';
  };

  watch(() => obj.brand,() => {
    console.log('监听的obj.brand.name改变了')
  },{
    deep:true,
    immediate:true,
  })
</script>

2. watchEffect 的使用

watchEffect 也是一个帧听器,是一个副作用函数。 它会监听引用数据类型的所有属性,不需要具体到某个属性,一旦运行就会立即监听,组件卸载的时候会停止监听。

<template>
  <div>
    <input type="text" v-model="obj.name"> 
  </div>
</template>

<script lang="ts" setup>
  import { reactive, watchEffect } from 'vue';

  let obj = reactive({
    name:'zs'
  });
  
  watchEffect(() => {
    console.log('name:',obj.name)
  })
 
</script>

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

watchEffect() 第二个参数是一个可选的配置对象,可以用来调整副作用的刷新时机或调试副作用的依赖

watchEffect((onCleanup) => {
    onCleanup(() => {
        console.log("清除副作用");
    })
}, {
    flush: 'post',  // sync  \ pre
    onTrack(e) {
    //被追踪为依赖时触发
        console.log(e);
    },
    onTrigger(e) {
    // 所追踪数据更改时触发
        console.log(e);
    }
})

3、停止侦听

当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。

在一些情况下,也可以显式调用返回值以停止侦听:手动停止一个侦听器,请调用 watchwatchEffect返回的函数:

<template>
  <div>
    <input type="text" v-model="obj.name"> 
    <button @click="stopWatchEffect">停止监听</button>
  </div>
</template>

<script lang="ts" setup>
  import { reactive, watchEffect } from 'vue';

  let obj = reactive({
    name:'zs'
  });

  const stop = watchEffect(() => {
    console.log('name:',obj.name)
  })

  const stopWatchEffect = () => {
    console.log('停止监听')
    stop();
  }
</script>

4、刷新时机

默认先执行watch,watchEffect监听器,然后更新DOM。

nextTick去获取组件更新完成之后的DOM,在watchEffect里就不需要用nextTick()(也没法用).

watch,watchEffect如果要操作“更新之后的DOM”,就要配置 flush: 'post'。

<template>
  <div>
    <div id="value">{{count}}</div> 
    <button @click="countAdd">增加</button>
  </div>
</template>

<script lang="ts" setup>
import { ref,watch,nextTick,watchEffect} from 'vue';
 
    let count = ref(0);
    const countAdd = () => {
      count.value++;
    }

    watch(count,() =>  {
      console.log('watch',count.value)
      nextTick(() =>{
        console.log('watch',document.querySelector('#value') && document.querySelector('#value').innerText)
      });
    },
    {immediate:true })

    watchEffect(() => {
      console.log('watchEffect',count.value)
      console.log('watchEffect',document.querySelector('#value') && document.querySelector('#value').innerText)
    },
    {flush: 'post'})
</script>