Vue3之watch和watchEffect实战总结

简介: Vue3之watch和watchEffect实战总结

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


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


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


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


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


watch监听单个数据


1. <template>
2. <input type="text" v-model="text1" />
3. </template>
4. 
5. <script setup> 
6. import { ref, watch } from 'vue'
7. const text1 = ref('')
8. 
9. watch(text1, (newVal, oldVal) => {console.log('监听单个数据', newVal, oldVal)
10.     }) 
11. </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>


监听对象的某一个值


1. <template>
2. name: <input type="text" v-model="student.name" />
3. <hr />
4. age: <input type="number" v-model="student.age" />
5. </template> 
6. <script lang="ts" setup>
7. import { reactive, watch } from 'vue'
8. const student = reactive({
9. name: '', age: ''
10. }) 
11. watch(() => student.name, (newVal, oldVal) => {
12. console.log('newVal', newVal)
13. console.log('oldVal', newVal)
14. }, {
15. deep: true, immediate: true
16. }) 
17. 
18. </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)

相关文章
|
13天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
13天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
22天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
50 7
|
23天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
41 3
|
22天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
39 1
|
22天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
44 1
|
7天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
vue学习第四章
|
7天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
vue学习第九章(v-model)
|
7天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
vue学习第十章(组件开发)
|
13天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
下一篇
无影云桌面