深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)

这篇具有很好参考价值的文章主要介绍了深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

#五一技术创作马拉松#

深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)

📋前言

计算属性 computed 和侦听器 watch 都是 Vue.js 框架中用来响应式更新视图的重要概念。在 Vue 项目开发中,这两个技术点是非常重要的,同时也是 Vue 基础中不可缺少的知识点。在面试中,计算属性 computed 和侦听器 watch也是经常出现的考点,作为前端开发也是必须要掌握的,这篇文章详细看看如何使用计算属性 computed 和侦听器 watch ,以及谈谈之前遇到的关于计算属性 computed 和侦听器 watch 的面试题。

📚相关文章:浅谈在 Vue2 和 Vue3 中计算属性和侦听器的一些变化
🔥官方介绍:计算属性 | Vue.js、侦听器 | Vue.js



🎯计算属性

🧩计算属性的定义

Vue.js 的插值在 HTML 模板中是支持 JavaScript 表达式的。但是如果当表达式过于复杂,模板代码就会变得非常臃肿且可读性差,难以维护。比如说,反转字符串这段代码。

<p>{{message.splice('').reverse().join('') }}</p>

这个表达式首先要将 message 变量按照每一个字母分解成一个字符串数组,然后将字符串数组翻转,接着再拼接字符串数组里面的内容成一个新的字符串,最终达到字符串翻转的效果。这里一个表达式就含有三个 JavaScript 方法,逻辑过于复杂,可读性非常差。

如果页面中有多个地方需要这个字符串翻转的表达式,那整体的 Vue 模板的代码将会变得非常难以维护,而且在 MVVM 模式下,模板中过多的处理数据计算,会导致模板和控制器之间高度耦合。这种情况下,就需要使用计算属性了。

计算属性的作用就是为了解决表达式逻辑过于复杂的场景,使页面尽可能没有数据复杂处理。计算属性是以函数的形式出现在组件的 computed 选项中,在新版的 Vue 3.x 的 setup 语法糖里,可以将计算属性的具体实现函数传递给 computed() 方法,然后赋值给一个变量。如果将上述翻转字符串的表达式通过计算属性实现,代码如下。

<template>
  <p>原始text:{{ message }}</p>
  <p>计算属性text:{{ reverseMessage }}</p>
</template>

<script setup>
import { computed, ref } from "vue";
const message = ref("hello world");
const reverseMessage = computed(() => {
  return message.value.split("").reverse().join("");
});
</script>

深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
这里的 reversedMessage 就是计算属性,它能够像其他变量一样,通过 Mustache 语法作为插值绑定到模板中。而且如果 message 变量被修改,对应的 reversedMessage 的结果也会立刻被修改。

🧩计算属性的用法

在 Vue 3.x 新版的 setup 语法中,计算属性有两种用法。第一种如上述的代码例子,直接将实现函数以参数的形式,然后传递给 computed() 方法中实现。另一种则是分别具体实现 getter 方法和 setter 方法,然后再封装成对象传递给 computed() 方法。

我们看下面这段代码,商品的单价在组件的 data 中 price 表示,通过计算属性,来看看买多个商品要多少钱。

<template>
  <p>单价:{{ price }} 元/件</p>
  <p>总价:{{ totalPrice }} 元</p>
</template>

<script setup>
import { computed, ref } from "vue";
const price = ref(6);
const totalPrice = computed({
  get: () => {
    return price.value * 5;
  },
  set: (value) => {
    price.value = value / 5;
  },
});
</script>

这里在 computed() 方法中接收了含义 get 变量和 set 变量的对象,其中 get 变量对应 getter 方法实现,set 则是对应 setter 方法实现。
深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
在浏览器中,可以使用 vue devtools 这个插件来手动修改 totalPrice 的值,然后可以看到页面的单价和总价都发生了变化。
深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
这里的数字变化是因为在代码中实现了 tolalPrice 计算属性的 selter 方法,当用户修改 totaiPrice 的数值,totalPricesetter 方法就会被调用,因而在 setter 内部被修改的 price 数值也会发生变化。但是此时整个调用链还没有结束,因为 price 的数值发生变动,触发了响应式机制,所以 totalPrice 的数值会根据最新的 price 数值重新计算,渲染最后的计算结果显示在页面上。从而达到单价和总价两个数字同时发生改变

这里有两个细节点需要注意:第一,如果计算属性没有实现 setter 方法,那么在 vue devtools 里计算属性变量是不可以被修改的,第二,如果被计算属性监测的变量在计算属性的 setter 方法内被修改,那么计算属性最终的数值会以被监测变量变化之后的最新数值重新计算或渲染。

🧩计算属性的缓存

计算属性的缓存是指,当计算属性依赖的数据发生变化时,计算属性会重新计算并缓存新的值。在缓存有效期内,如果再次访问该计算属性,则直接返回缓存中的值,不需要重新计算。

❗注意:如果计算属性函数中的数据不是响应式依赖,就不会触发计算属性的缓存机制。比如 Date().now()Date().getTime() 都不是响应式依赖,因此它们的计算属性不会更新

这里我们可以看一个很经典的对比,对比看计算属性的缓存和方法,看看页面渲染的效果。我们以下面这段代码作为案例。

<template>
  <p>单价:{{ price }} 元/件</p>
  <p>computed总价:{{ totalPrice }} 元</p>
  <p>computed总价:{{ totalPrice }} 元</p>
  <p>computed总价:{{ totalPrice }} 元</p>
  <p>methods总价:{{ methods() }} 元</p>
  <p>methods总价:{{ methods() }} 元</p>
  <p>methods总价:{{ methods() }} 元</p>
</template>

<script setup>
import { computed, ref, watch, watchEffect } from "vue";
const price = ref(6);
const totalPrice = computed(() => {
  console.log("调用computed()");
  return price.value * 5;
});
const methods = () => {
  console.log("调用methods()");
  return price.value * 5;
};
</script>

深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
如上图,我们可以看到页面渲染效果和控制台打印的信息,计算属性的函数只调用了一次,而普通的方法函数则调用了多次。原因就是计算机属性将基于它的响应式依赖关系缓存。即便是多次使用只需要计算一次,而方法则是用多少次就会被执行多少次。

这种缓存机制可以有效地提高应用程序的性能。例如,如果计算属性需要遍历一个大型数组并进行复杂的运算,而且这个数组的数据变化频繁,如果每次都重新计算,会极大地降低应用程序的响应速度。但是,如果使用了计算属性的缓存机制,只有数组数据发生变化时才会重新计算,大大节约了计算资源和时间。

需要注意的是,如果计算属性依赖的数据未被定义为响应式的,或者计算属性的 getter 函数中包含了异步操作或者时间较长的同步操作,那么计算属性的缓存可能会失效或者带来其他副作用。因此,需要根据实际情况谨慎使用计算属性的缓存机制,或者使用其他方式来解决以上问题。面对这种情况,我们换一种思路来使用侦听器。


🎯侦听器

🧩侦听器性的定义

通常情况下,我们可以使用计算属性来处理数据的计算和衍生,但是在某些情况下,计算属性并不够灵活,不能满足我们的需求。例如,需要在数据变化时执行异步操作、发送网络请求或者操作 DOM 等。这个时候,我们可以使用侦听器来处理这些需求。

在 Vue.js 中,侦听器是一个函数,它可以监听响应式数据的变化,当数据发生变化时,侦听器会被触发执行。下面是一个使用侦听器的例子。

<template>
  <input type="number" v-model="num" />
  <p>result为:{{ result }}</p>
</template>

<script setup>
import { ref, watch } from "vue";
const num = ref(0);
const result = ref(0);
watch(
  () => num.value,
  (newValue, oldValue) => {
    console.log("旧值为:", oldValue, "新值为:", newValue);
    return (result.value = num.value * num.value);
  }
);
</script>

深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
上面的代码,我们通过输入框输入数字修改 num 值,侦听器通过监听 num 值的变化,计算输入数字的结果。这个功能与计算属性很类似,但区别是侦听器可以检测数值的变化,以及在控制台的输出。

侦听器其实是计算属性的底层逻辑实现。不同之处在于侦听器可以知道变化前后的数据数值以及在侦听器之内可以使用异步方法,在侦听器里添加异步操作是一个很重要的操作,这里可以是网络请求,也可以是开销较大的计算,这是计算属性中没有的。

🧩侦听器的用法

在 Vue 3.x 的 setup 语法中,侦听器的语法如下。

watch{
	被监测属性,
	监测到变化时的回调函数,
	参数选项
}

其中,第二个监测到变化时的回调函数的可传参数如下。

() => {
	// 无参数
}
(value) => {
	// 变化后的数值作为参数传入
}
(newValue, oldValue) => {
	// 变化前的数值和变化后的数值作为参数传入
}

所以侦听器不仅可以监测基本数据类型的 ref() 数据,同时还可以监测引用数据类型的 reactive() 数据。不过监测引用数据类型的数据,需要将 { deep: true } 属性作为第三个参数传给 watch() 函数。

{ deep: true } 选项可以告诉 Vue 在监测数据变化时,递归到深层嵌套的属性值中去。这样即使属性值是引用类型,只要其内部的属性值发生了变化,watch 回调函数也能够正常触发执行。看下面这段代码。

<template>
  <div>
    <p>user: {{ data.user }}</p>
    <button @click="updateUser">Update User</button>
  </div>
</template>

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

const data = reactive({
  user: {
    name: "张三",
    age: 20,
    hobby: ["唱歌", "跳舞"],
  },
});

watch(
  () => data.user,
  (newValue, oldValue) => {
    console.log("user changed", newValue, oldValue);
  },
  { deep: true }
);

function updateUser() {
  data.user.name = "李四";
  data.user.hobby.push("游泳");
}
</script>

深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
在这个例子中,我们创建了一个包含嵌套属性的 reactive()数据对象,并使用 watch 监测了 user 属性。在 watch 选项中,我们使用 { deep: true } 选项来告诉 Vue 在监测数据变化时递归到嵌套属性中去。

当用户点击 Update User 按钮时,updateUser 函数会更新 user 属性中的 namehobby 属性值。这里注意,我们使用了 data.user.namedata.user.hobby.push() 的方式来更新属性值,而不是直接赋值一个新的对象。这样就能够确保 user 属性的引用地址不变,从而让 deep 选项生效。
深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
此时,由于我们监测了 user属性,并使用了 { deep: true } 选项,当 user 属性中的嵌套属性值发生变化时,watch 回调函数就会被触发执行,打印出相应的信息。

❗注意:深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。

即时回调的侦听器​
watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。

我们可以通过传入 immediate: true 选项来强制侦听器的回调立即执行:

watch(source, (newValue, oldValue) => {
  // 立即执行,且当 `source` 改变时再次执行
}, { immediate: true })

🧩watchEffect() 函数

侦听器的回调使用与源完全相同的响应式状态是很常见的。例如下面的代码,在每当 todoId 的引用发生变化时使用侦听器来加载一个远程资源:

const todoId = ref(1)
const data = ref(null)

watch(todoId, async () => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
  )
  data.value = await response.json()
}, { immediate: true })

特别是注意侦听器是如何两次使用 todoId 的,一次是作为源,另一次是在回调中。

我们可以用 watchEffect 函数 来简化上面的代码。watchEffect()允许我们自动跟踪回调的响应式依赖。上面的侦听器可以重写为:

watchEffect(async () => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
  )
  data.value = await response.json()
})

这个例子中,回调会立即执行,不需要指定 immediate: true。在执行期间,它会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。有了 watchEffect(),我们不再需要明确传递 todoId 作为源值。

对于这种只有一个依赖项的例子来说,watchEffect() 的好处相对较小。但是对于有多个依赖项的侦听器来说,使用 watchEffect() 可以消除手动维护依赖列表的负担。此外,如果你需要侦听一个嵌套数据结构中的几个属性,watchEffect() 可能会比深度侦听器更有效,因为它将只跟踪回调中被使用到的属性,而不是递归地跟踪所有的属性。

✅TIP:watchEffect 仅会在其同步执行期间,才追踪依赖。在使用异步回调时,只有在第一个 await 正常工作前访问到的属性才会被追踪。

🧩watch 与 watchEffect

watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:

  • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
  • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

🧩停止侦听器

setup()<script setup> 中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。因此,在大多数情况下,你无需关心怎么停止一个侦听器。

一个关键点是,侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。如下方这个例子:

<script setup>
import { watchEffect } from 'vue'

// 它会自动停止
watchEffect(() => {})

// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>

要手动停止一个侦听器,请调用 watchwatchEffect 返回的函数:

const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()

注意,需要异步创建侦听器的情况很少,请尽可能选择同步创建。如果需要等待一些异步数据,你可以使用条件式的侦听逻辑:

// 需要异步请求得到的数据
const data = ref(null)

watchEffect(() => {
  if (data.value) {
    // 数据加载后执行某些操作...
  }
})

🎯二者区别

通过上面的内容和例子,我们算是简单了解和学习了计算属性和侦听器,在前端开发的面试中,计算属性和侦听器也是经常出现的考核点,计算属性和侦听器的区别则是面试官经常问道的题目。通过上面的内容,我们也大概可以把这些区别说明白了,接下来我们简单总结一下。

❓问:Vue 中计算属性和侦听器的区别是什么?

📝答:如下。

  • 计算属性是基于它所依赖的响应式数据动态计算的值,只有在计算属性所依赖的数据发生变化时才会重新计算,且计算结果会被缓存起来。相比之下,侦听器监听的是某个特定的数据变化,当该数据变化时,会触发执行回调函数。

  • 计算属性定义的是 getter 函数,只能用于获取数据,不能用于设置数据。而侦听器定义的是回调函数,可以进行任何逻辑处理,包括修改数据本身或者执行其他操作。

  • 在使用场景上,计算属性通常用于将数据转换为显示需要的格式,而侦听器则适合处理需要执行异步或复杂逻辑的情况。


📝最后

计算属性 computed 和侦听器 watch 都是实现响应式编程的重要工具,可以方便地处理复杂的业务数据变化场景。了解它们的区别和应用场景,能够帮助我们更好地设计和优化我们的 Vue 组件和应用。
深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)
文章来源地址https://www.toymoban.com/news/detail-472164.html

到了这里,关于深入与浅谈 Vue 中计算属性和侦听器的区别和使用(Vue3版本为例)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【vue2】计算属性(computed)与侦听器(watch)详解

    🥳博       主: 初映CY的前说(前端领域) 🌞个人信条: 想要变成得到,中间还有做到! 🤘本文核心: 计算属性与侦听属性的用法 目录( 文末有给大家准备好的Xmind思维导图 ) 一、计算属性computed ①默认get()方法,仅是获取值 ②不仅仅是获取值,还具有修改属性功能

    2024年01月16日
    浏览(35)
  • 前端Vue入门-day02-vue指令、computed计算属性与watch侦听器

    (创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,请留下您的足迹) 目录 指令补充 指令修饰符 v-bind 对于样式控制的增强  操作class 案例:京东秒杀 tab 导航高亮 操作style  v-model 应用于其他表单元素  computed 计算属性 基础语法 computed 计算属

    2024年02月11日
    浏览(32)
  • Vue3 计算属性和侦听器实战(computed、watch)——简易点餐页面

    这篇文章记录一下 Vue3 计算属性和侦听器 (computed、watch) 实战的内容,这篇文章我们在有计算属性和侦听器的基础上,我们来制作一个简易点餐页面,接下来我们一起来从零到一开始制作。 计算属性和侦听器相关文章推荐: 深入与浅谈 Vue 中计算属性和侦听器的区别和使用

    2024年02月09日
    浏览(79)
  • Vue——侦听器

    目录 基本示例​ 深层侦听器​ 即时回调的侦听器​ 回调的触发时机​ this.$watch()​ 停止侦听器​        计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态

    2023年04月13日
    浏览(36)
  • Vue中watch侦听器用法

    watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用 watch第一个参数监听源 watch第二个参数回调函数cb(newVal,oldVal) watch第三个参数一个options配置项是一个对象{ immediate :true //是否立即调用一次 deep :true //是否开启深度监听 flush :“pre” // 更新时机 } flush配置项 p

    2024年02月06日
    浏览(37)
  • 【源码系列#04】Vue3侦听器原理(Watch)

    专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核💪推荐🙌 欢迎各位ITer关注点赞收藏🌸🌸🌸 侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数 第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性

    2024年02月04日
    浏览(34)
  • Vue Composition API之侦听器watch/watchEffect

    在日常的开发中,很多时候我们需要去对一些状态进行监听,比如当显示学生的成绩列表时,我们使用一个学生的学号student_num作为请求成绩的参数,如果没有监听机制,当学号student_num改变时,我们需要依赖用户的操作去刷新成绩。但是有了侦听器,我们可以通过侦听器去监

    2024年02月08日
    浏览(29)
  • Vue3前端开发,watch数据侦听器的立即执行

    Vue3前端开发,watch数据侦听器的立即执行!实际上,我们可以通过回调函数的后面,再追加一个对象,来设置这个参数,immediate:true.来实现一种立即执行的效果。在页面记载完成后,马上就会执行一次watch. 如上所示,我们在回调函数的后面,追加了一个参数,是一个对象类型

    2024年01月18日
    浏览(38)
  • 微信小程序实现页面数据侦听器,类似vue的watch

    既然小程序的组件已经有Observer功能,那为什么还要手写watch功能呢? Observer只能在Component中使用,没法在Page中使用。若是想在Page中监控某一数据的变化,Observer做不到。 Observer属于小程序的新功能,只能在高版本微信使用,低版本微信无法使用。公司的小程序就因为使用了

    2024年02月03日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包