【前端学习指南】Vue computed 计算属性 watch 监听器
🍭 Hello,我是爱吃糖的范同学
🔴 想把自己学习技术的经历和一些总结分享给大家!
🔴 通过这样的方式记录自己成长,同时沉淀自己的技术,我会把所有额外的时间和经历投放到CSDN和公众号(💥公号名:AIRC Team💥,欢迎关注,为大家准备了很多有关编程学习资料)文章的撰写。
🔴 希望能通过这样的方式让大家认识我,和我一起学习编程,共同进步!😃
希望能和大家一起进步和成长!坚持热爱!🎉🎉🎉
目录
✏️ computed 计算属性:
✏️ watch 监听器:
1.定义一个watch监听器:
2.immediate 选项:
3.deep 选项:
4.监听对象单个属性变化:
✏️ 计算属性与监听器对比 :
1.适用场景
2. 特性对比
2.1 计算属性
2.2 监听器
3.何时选择计算属性,何时选择监听器
✏️ 写在最后:
✏️ 往期文章:
✏️ computed 计算属性:
计算属性本质上就是一个 function 函数,它可以实时监听 data 中的数据变化,并 return 一个计算后的新值提供给组件,组件会对这个新值进行 DOM 渲染。
1.声明一个计算属性:
计算属性需要以 function 函数的形式声明到组件的 computed 选项中。
<template><div><h1>{{ fullName }}</h1><input v-model="firstName" placeholder="Enter first name"><input v-model="lastName" placeholder="Enter last name"></div>
</template><script>
export default {data() {return {firstName: '',lastName: ''};},computed: {fullName() {return `${this.firstName} ${this.lastName}`;}}
};
</script><style scoped>
/* Optional CSS styles */
</style>
data
中定义了 firstName
和 lastName
,它们会绑定到两个输入框的值。computed
中定义了一个计算属性 fullName
,它会根据 firstName
和 lastName
的值动态计算并返回一个拼接后的全名。当你输入名字时,fullName
会自动更新并显示在页面上。
计算属性是基于依赖的响应式数据来缓存的,只有相关的数据发生变化时,它们才会重新计算,从而提高性能。
如果是使用的Vue3 的 setup 语法糖怎么进行声明捏???
在 vue3 中,computed 可以在 setup 函数里实现。除了写法不一样,功能上与 vue2 中的 computed 是一致
-
只有 getter 时,传入一个回调函数。
-
有 getter 和 setter时,传入一个对象,有 get 和 set 两个属性方法。
-
需要将处理后的值返回作为该计算属性的值。
在 setup 里使用 computed 的三种方式:
(1)直接在 setup 里使用 computed 函数;
<template><div>{{ fullName }}</div>
</template><script>import { ref, computed } from 'vue'export default {setup() {const firstName = ref('hello')const lastName = ref('world')const fullName = computed(() => {return firstName.value + '-·-' + lastName.value})return {firstName,lastName,fullName}}}
</script>
(2)通过 defineComponent 函数在 setup 里使用 computed 函数
defineComponent 函数是 vue3 的语法糖:
PS:defineComponent 函数支持 TS 的 “参数类型推断”(如果你使用的是 vue3 + TS,那么使用 defineComponent 将会更友好。)
<template><div>{{ fullName }}</div>
</template><script>import { defineComponent, ref, computed } from 'vue'export default defineComponent({setup() {const firstName = ref('hello')const lastName = ref('world')const fullName = computed(() => {return firstName.value + '-·-' + lastName.value})return {firstName,lastName,fullName}}})
</script>
(3)在 <script setup> 里使用 computed 函数
<script setup> 是 vue3 的新的语法糖,之前的组合 API 相比:
-
之前的组合 API 必须返回
return
,使用 <script setup> 后就不必了。 -
有更好的运行时性能。
<template><div>{{ fullName }}</div>
</template><script setup>import { ref, computed } from 'vue'const firstName = ref('hello')const lastName = ref('world')const fullName = computed(() => {return firstName.value + '-·-' + lastName.value})
</script>
在 setup 里的 computed 的 getter 和 setter:
当 computed 有 getter 和 setter 时,需要传入一个对象而不是一个函数作为 computed 的参数,然后在 computed 中实现 get 和 set 两个属性方法。
<template><div> firstName: {{ firstName }} </div><div> lastName: {{ lastName }} </div><div> fullName: {{ fullName }} </div>
</template><script>import { reactive, toRefs, computed } from 'vue'export default {setup() {const user = reactive({firstName: 'hello',lastName: 'world'})const fullName = computed({get() {return user.firstName + '-·-' + user.lastName},set(val) {const nameList = val.split('-·-')user.firstName = nameList[0]user.lastName = nameList[1]}})return {...toRefs(user),fullName}}}
</script>
2.计算属性与方法对比:
计算属性会对计算执行结果进行缓存,只有计算属性的依赖项(原始数据来源)发生变化时,才会重新计算。否则,都是直接获取当前缓存的计算结果。而无需重新执行计算逻辑。相对于方法来说,计算属性的性能比方法更好。
-
计算属性和方法的调用方式不同: 计算属性是通过对应名称直接访问的,而方法需要在模板中使用
v-on
指令或者@
符号进行调用。 -
计算属性可以缓存结果,而方法不能: 计算属性的值会被缓存起来,只有在它所依赖的响应式数据变化时才会重新计算。而方法每次调用都要重新计算,无法被缓存。
-
计算属性应该用于简单的计算,而方法用于复杂逻辑的处理: 如果需要对数据进行一些简单的计算或过滤,可以使用计算属性,因为它们比方法更高效。而如果需要进行一些复杂的逻辑处理,应该使用方法。
✏️ watch 监听器:
watch 监听器允许开发者监视数据的变化,从而针对数据的变化进行特定的操作。例如可以监听输入框的输入信息,发起 Ajax 请求返回和输入内容相关的搜索信息。
1.定义一个watch监听器:
message
被监听,一旦它的值发生变化,watch
会调用对应的回调函数,newValue
是新值,oldValue
是旧值。可以在这个回调中做其他逻辑操作,比如发起异步请求、验证数据、或者进行其他逻辑处理。
这种方式适用于当你需要在数据变化时执行一些操作,且该操作不适合放在 computed
中的情况。
<template><div><input v-model="message" placeholder="Enter a message"><p>Message: {{ message }}</p><p>Message length: {{ messageLength }}</p></div>
</template><script>
export default {data() {return {message: ''};},computed: {messageLength() {return this.message.length;}},watch: {// 监听 message 的变化message(newValue, oldValue) {console.log(`Message changed from "${oldValue}" to "${newValue}"`);// 这里可以加入一些逻辑,比如发送请求或者做其他操作}}
};
</script><style scoped>
/* Optional CSS styles */
</style>
data
中定义了 message
,它会绑定到一个输入框,允许用户输入文本。computed
中定义了 messageLength
,计算并返回 message
的长度。watch
监听器监听 message
的变化。当用户输入时,watch
会触发,打印出 message
之前和之后的值。
在 setup 语法糖中使用监听器:
使用 watch
来监听 message
的变化。当 message
的值发生变化时,回调函数会触发,打印出旧值和新值。
设置 immediate: true
,使得监听器在组件创建时立即触发一次,打印出 message
的初始值变化。
返回值:在 setup
中返回了 message
和 messageLength
,这些值可以在模板中使用。
<template><div><input v-model="message" placeholder="Enter a message"><p>Message: {{ message }}</p><p>Message length: {{ messageLength }}</p></div>
</template><script>
import { ref, watch } from 'vue';export default {setup() {// 定义响应式数据const message = ref('Hello, Vue!');// 计算属性的实现const messageLength = computed(() => message.value.length);// 使用 watch 监听 `message` 变化,immediate 设置为 truewatch(message,(newValue, oldValue) => {console.log(`Message changed from "${oldValue}" to "${newValue}"`);},{ immediate: true } // 初始化时立即触发);// 返回需要在模板中使用的数据和方法return {message,messageLength};}
};
</script><style scoped>
/* Optional CSS styles */
</style>
2.immediate 选项:
默认情况下,组件在初次加载完毕后不会调用 watch 监听器。如果希望 watch 监听器能够立即被调用,则需要使用 immediate 选项。
<template><div><input v-model="message" placeholder="Enter a message"><p>Message: {{ message }}</p><p>Message length: {{ messageLength }}</p></div>
</template><script>
export default {data() {return {message: 'Hello, Vue!'};},computed: {messageLength() {return this.message.length;}},watch: {// 监听 message 的变化,immediate 设置为 truemessage: {handler(newValue, oldValue) {console.log(`Message changed from "${oldValue}" to "${newValue}"`);},immediate: true // 在初始化时立即触发}}
};
</script><style scoped>
/* Optional CSS styles */
</style>
handler
是实际的监听函数,它会打印出 message
的变化。immediate: true
会使得监听器在组件创建时就立即执行一次,而不仅仅是在 message
发生变化时。
3.deep 选项:
当 watch 监听的是一个对象,如果对象的属性值发生了变化,上面的配置情况下是无法监听到变化的。此时需要使用 deep 选项。
<template><div><input v-model="user.name" placeholder="Enter your name" /><input v-model="user.age" placeholder="Enter your age" /><p>User Info: {{ user.name }} - {{ user.age }}</p></div>
</template><script>
import { ref, watch } from 'vue';export default {setup() {// 定义一个包含嵌套属性的响应式对象const user = ref({name: 'John Doe',age: 30});// 使用 deep 选项来监听对象的深层变化watch(user,(newValue, oldValue) => {console.log('User info changed:', newValue);},{ deep: true } // 开启深度监听);return {user};}
};
</script><style scoped>
/* Optional CSS styles */
</style>
响应式对象:user
是一个包含 name
和 age
属性的对象,通过 ref
包装为响应式对象。
watch
使用:我们使用 watch
来监听 user
对象的变化,并设置了 { deep: true }
选项,意味着如果 user
对象的任何嵌套属性发生变化,监听器都会被触发。
回调函数:每次 user
对象发生深层次变化时(比如修改了 name
或 age
),回调函数就会打印新的 user
对象。
为什么使用 deep
:
-
浅层监听:默认情况下,
watch
只会监听对象或数组的引用变化。例如,如果你直接修改了user.name
的值,watch
会被触发。 -
深层监听:使用
deep: true
选项后,即使修改了嵌套的属性(例如user.name
或user.age
),watch
也会检测到变化并触发回调。这对于复杂的数据结构非常有用,特别是当你需要监听嵌套的对象或数组时。
注意事项:
-
开启
deep
选项会带来一定的性能开销,尤其是在对象或数组较大时,因为它需要对整个对象或数组进行递归遍历和监控。因此,在使用时要谨慎,避免不必要的性能损失。 -
适合用于需要对深层次数据进行监控的场景,比如表单数据、复杂对象等。
4.监听对象单个属性变化:
在使用 deep 选项后,会对整个对象中的所有属性值的变化进行监听,但是处于某种场景下,只希望对对象中某个属性进行监听,此时可以按照如下规则进行定义 watch 监听器:
<template><div><input v-model="user.name" placeholder="Enter your name" /><p>User Name: {{ user.name }}</p></div>
</template><script>
export default {data() {return {user: {name: 'John Doe',age: 30}};},watch: {// 监听 user.name 属性变化'user.name': function (newValue, oldValue) {console.log(`User name changed from "${oldValue}" to "${newValue}"`);}}
};
</script><style scoped>
/* Optional CSS styles */
</style>
如果你想监听多个属性,并且希望在属性变化时做统一的处理,可以使用 watch
的 对象形式,将多个属性的监听逻辑写在一个 handler 中。例如:
<script>
export default {data() {return {user: {name: 'John Doe',age: 30}};},watch: {// 监听 user 对象中的多个属性变化'user': {handler(newUser, oldUser) {if (newUser.name !== oldUser.name) {console.log(`Name changed: ${oldUser.name} -> ${newUser.name}`);}if (newUser.age !== oldUser.age) {console.log(`Age changed: ${oldUser.age} -> ${newUser.age}`);}},deep: true // 深度监听整个 user 对象的变化}}
};
</script>
✏️ 计算属性与监听器对比 :
计算属性(computed)和监听器(watch)是 Vue 中用于处理数据变化的两种常用方式。它们各自有不同的用途和特性,理解它们的差异能帮助你在开发中做出更合适的选择。
-
计算属性(computed):
-
用于基于已有数据计算派生数据(例如,多个数据项的组合结果)。
-
计算属性会缓存计算结果,只有当其依赖的数据变化时才会重新计算。这使得它们在性能上更加高效,避免不必要的计算。
-
计算属性是 惰性求值,即只有在它们被访问时才会计算,并且会缓存直到依赖项发生变化。
-
-
监听器(watch):
-
用于在数据变化时执行 异步操作 或执行副作用(比如向服务器发起请求)。
-
监听器可以监听 某个数据属性的变化,并在变化时执行指定的回调函数。
-
监听器是 立即执行 的,即数据变化后会立即触发回调函数。
-
1.适用场景
-
计算属性:当你需要根据一个或多个数据属性的值进行计算,且计算结果依赖的数据不经常变化时,使用计算属性更合适。它主要用于:
-
从已有数据计算出新的值(例如,格式化日期、计算总金额等)。
-
需要缓存计算结果,避免重复计算的场景。
-
-
监听器:当你需要执行异步操作、或者某些副作用操作时(如发起 HTTP 请求、操作 DOM 或者调用外部 API),使用监听器更为合适。它适用于:
-
监控数据变化并执行副作用(例如,当某个输入框的值变化时触发表单提交)。
-
需要对某些数据变化进行处理,而不仅仅是计算派生数据。
-
2. 特性对比
特性 | 计算属性 (computed) | 监听器 (watch) |
---|---|---|
主要用途 | 计算派生数据,避免重复计算。 | 监听数据变化并执行副作用(如异步操作)。 |
缓存 | 会缓存计算结果,只有依赖的数据变化时才重新计算。 | 不缓存,每次数据变化都会执行回调函数。 |
执行时机 | 在模板渲染时或者访问时计算。 | 在数据变化时立即执行回调函数。 |
返回值 | 必须返回一个值,这个值通常用在模板中或其他计算属性中。 | 无返回值,通常用来触发副作用(如发起请求、修改 DOM)。 |
异步操作 | 不适合处理异步操作。 | 非常适合处理异步操作。 |
复杂计算 | 适合简单的、基于已有数据的计算。 | 不适合做复杂的计算,更多用于处理副作用。 |
性能 | 性能较好,因其计算结果会被缓存。 | 每次数据变化都会执行,可能影响性能。 |
2.1 计算属性
<template><div><p>{{ fullName }}</p></div>
</template><script>
export default {data() {return {firstName: 'John',lastName: 'Doe'};},computed: {fullName() {// 计算并返回 fullName,只会在 firstName 或 lastName 改变时重新计算return this.firstName + ' ' + this.lastName;}}
};
</script>
-
解释:
fullName
是一个计算属性,它基于firstName
和lastName
来计算,并且会在这两个数据发生变化时重新计算。计算属性会缓存计算结果,避免多次计算。
2.2 监听器
<template><div><input v-model="name" placeholder="Enter your name"></div>
</template><script>
export default {data() {return {name: ''};},watch: {// 监听 name 的变化,并在变化时执行回调函数name(newName) {console.log('Name changed to: ' + newName);// 可以在这里执行异步操作,比如发起 HTTP 请求等}}
};
</script>
-
解释:
watch
监听name
的变化,当name
的值发生变化时,会执行指定的回调函数。在这个例子中,我们简单地打印了新值,但也可以在回调中执行更复杂的操作,比如向服务器发送请求。
3.何时选择计算属性,何时选择监听器
选择计算属性:
-
当你需要在模板中显示一个基于其他数据的计算值时。
-
当你需要避免重复计算时(缓存计算结果)。
-
当你不需要执行副作用(例如异步操作或修改外部状态)时。
选择监听器:
-
当你需要在数据变化时执行副作用操作时(如发送 HTTP 请求、修改 DOM、触发动画等)。
-
当你需要在数据变化后进行复杂的操作,而不仅仅是计算派生数据时。
-
当你需要监听的数据是动态变化的,不适合缓存时。
组合使用:
有时,你可能需要同时使用计算属性和监听器。例如,可以使用计算属性来生成数据,使用监听器来处理外部副作用。
<template><div><input v-model="name" placeholder="Enter your name"><p>{{ greeting }}</p></div>
</template><script>
export default {data() {return {name: ''};},computed: {// 使用计算属性生成问候语greeting() {return this.name ? `Hello, ${this.name}!` : 'Hello!';}},watch: {// 监听 name 的变化,触发异步请求name(newName) {if (newName) {this.fetchGreeting(newName);}}},methods: {async fetchGreeting(name) {// 假设这里发起了一个异步请求console.log(`Fetching greeting for ${name}...`);}}
};
</script>
✏️ 写在最后:
学习是通往未来的桥梁,是打开世界大门的钥匙。每一步的坚持,都是向梦想迈进的一小步。即使今天的努力看不见回报,明天的你会感谢今天的自己。记住,每一个挑战都是成长的机会,每一次进步都是成功的积累。别怕慢,只有不前行才会停滞。相信自己的潜力,勇敢追求知识,你将会看到一个不一样的、更强大的自己!
✏️ 往期文章:
2024年还在问前端怎么学?一份前端学习指南_web前端怎么学习人工智能-CSDN博客
【前端学习指南】基础开发环境搭建_前端项目搭建环境-CSDN博客
【前端学习指南】开启 Vue 的学习之旅_前端vue 学习之旅-CSDN博客
【前端学习指南】第一站 Vue 生命周期初探_vue 前端学习-CSDN博客
【前端学习指南】第二站 Vue 工程化开发-CSDN博客
【前端学习指南】第三站 Vue 组件之间通信-CSDN博客
【前端学习指南·番外篇】Node.js 安装及环境配置_node前端运行环境-CSDN博客