首页 > 编程笔记 > JavaScript笔记
阅读:10
Vue计算属性和侦听器详解(附带实例)
大多数情况下,我们都可以将 Vue 组件中定义的属性数据直接渲染到 HTML 元素上,但是有些场景下,属性中的数据并不适合直接渲染,需要我们处理后再进行渲染。
在 Vue 中,通常使用计算属性或侦听器来实现这种逻辑。
在 Vue 中,与存储属性相对的是计算属性。计算属性不用于存储数据,而是根据定义的计算逻辑来实时更新其值。
假设我们需要在组件中定义一个 type 属性,该属性的值依赖于 count 属性的值。如果 count 不大于 10,type 属性的值应为“小”;否则,type 的值应为“大”。实现这一功能的一种方法是在模板中使用JavaScript表达式,但这种方法更适合处理简单逻辑。
对于更复杂的逻辑,使用计算属性会更加合适。示例代码如下:
计算属性的关键在于,它们是基于存储属性的值通过逻辑运算得出的。当任何影响计算属性值的存储属性发生变化时,计算属性会自动更新。如果有任何元素绑定到计算属性上,这些元素也会自动同步更新以反映新的值。
计算属性的强大之处在于它们的响应性。Vue 的依赖跟踪系统确保了当相关的存储属性变化时,所有依赖于这些属性的计算属性都会重新计算,并且相关的视图更新也会自动发生。
例如,编写 HTML 代码如下:
HTML 元素:
Vue 组件定义:
相比之下,函数每次被调用时都会执行其内部的逻辑代码,并返回新的计算结果。因此,函数的结果不会像计算属性那样被缓存。
在实际应用中,我们可以根据是否需要缓存结果这一标准来选择使用计算属性或函数。如果某个值的计算成本较高,且不经常变化,使用计算属性可以提高性能。相反,如果每次访问都需要最新的结果,或者计算非常简单,使用函数可能更合适。
通常,计算属性只用来取值,不会用来存值,因此计算属性默认提供的是取值的方法,我们称之为 get 方法。但是这并不代表计算属性不支持赋值,计算属性也可以通过赋值进行存值操作,存值的方法我们需要手动实现,通常称之为 set 方法。
例如,修改前面编写的代码中的 type 计算属性如下:
可以直接使用组件实例进行计算属性 type 的赋值,赋值时会调用我们定义的 set 方法,从而实现对存储属性 count 的修改,示例代码如下:
但是,需要额外注意的是,如果一个计算属性仅实现了 get 方法而没有实现 set 方法,则它只能用于取值,不能用于赋值。在 Vue 中,这类仅实现 get 方法的计算属性也被称为只读属性。如果我们尝试对一个只读属性进行赋值操作,Vue 会捕获这个操作并发出警告。
例如,如果我们尝试对只读的计算属性 type 赋值,控制台将输出以下异常信息:
举例来说,许多人在使用互联网时都会使用搜索引擎。以百度搜索引擎为例,当我们在搜索框中输入关键字时,网页会自动显示一些推荐词汇供用户选择,如下图所示。

图 1 搜索引擎的推荐词功能
要实现这一功能,就需要对用户的输入行为进行监听,这正是侦听器发挥作用的理想场景。
在 Vue 中,我们可以定义一个侦听器来观察特定数据的变化。当数据变化时,侦听器会自动触发,允许我们执行定义好的逻辑,比如根据用户的输入动态显示搜索建议。
在定义 Vue 组件时,可以通过 watch 函数来定义属性侦听器,首先,创建一个名为 2.watch.html 的文件,在其中编写如下测试代码:
运行上面的代码,尝试在页面的输入框中输入一些字符,可以看到当输入框中的字符超过 10 个时,就会有警告框弹出,提示输入文本过长,如下图所示:

图 2 属性侦听器应用示例
从一些特性来看,属性侦听器和计算属性有类似的应用场景,使用计算属性的 set 方法也可以实现与上面示例代码类似的功能。
在 Vue 中,通常使用计算属性或侦听器来实现这种逻辑。
Vue计算属性
在前面的章节中,我们定义的属性是存储属性,它们直接存储了我们定义的值。存储属性的作用就是保持这些值。在 Vue 中,与存储属性相对的是计算属性。计算属性不用于存储数据,而是根据定义的计算逻辑来实时更新其值。
假设我们需要在组件中定义一个 type 属性,该属性的值依赖于 count 属性的值。如果 count 不大于 10,type 属性的值应为“小”;否则,type 的值应为“大”。实现这一功能的一种方法是在模板中使用JavaScript表达式,但这种方法更适合处理简单逻辑。
对于更复杂的逻辑,使用计算属性会更加合适。示例代码如下:
// 从 Vue 中解构所需的 API const { createApp, ref, computed } = Vue; // 定义组件配置 const config = { setup() { const count = ref(0); // 定义响应式数据 count,初始值为 0 const add = () => { // 定义方法 add count.value++; // 每次调用 count 自增 1 }; // 定义只读的计算属性 type const type = computed(() => { return count.value > 10 ? "大" : "小"; }); // 暴露给模板的变量和方法 return { count, add, type }; } }; // 创建组件并获取组件实例 let instance = createApp(config).mount("#Application"); // 像使用正常属性一样使用计算属性 console.log(instance.type);在上述代码中,我们使用 Vue 的 computed 函数来定义计算属性。此函数接收一个 get 方法,该方法具体指定了计算属性的值是如何根据逻辑计算得出的。我们可以像访问普通属性一样访问计算属性。
计算属性的关键在于,它们是基于存储属性的值通过逻辑运算得出的。当任何影响计算属性值的存储属性发生变化时,计算属性会自动更新。如果有任何元素绑定到计算属性上,这些元素也会自动同步更新以反映新的值。
计算属性的强大之处在于它们的响应性。Vue 的依赖跟踪系统确保了当相关的存储属性变化时,所有依赖于这些属性的计算属性都会重新计算,并且相关的视图更新也会自动发生。
例如,编写 HTML 代码如下:
<div id="Application"> <div>{{type}}</div> <button @click="add">Add</button> </div>运行代码,单击页面上的按钮,当组件 count 的值超过 10 时,页面上对应的文案会更新成“大”。
使用计算属性还是函数
对于前面示例的场景,我们也可以通过定义一个函数来实现相应的功能。示例代码如下:HTML 元素:
<div id="Application"> <div>{{typeFunc()}}</div> <button @click="add">Add</button> </div>
Vue 组件定义:
// 从 Vue 中解构所需的 API const { createApp, ref, computed } = Vue; // 定义组件 const config = { setup() { const count = ref(0); // 定义响应式数据 count,初始值为 0 const add = () => { // 定义方法 add count.value++; }; // 定义只读的计算属性 type const type = computed(() => { return count.value > 10 ? "大" : "小"; }); // 此函数根据 count 的值决定返回值 const typeFunc = () => { return count.value > 10 ? "大" : "小"; }; // 暴露给模板的变量和方法 return { count, add, type, typeFunc }; } };从输出结果来看,使用函数和计算属性在某些情况下可能产生相同的效果。但实际上,计算属性依赖于其依赖的存储属性的值。当依赖的值发生变化时,计算属性会重新计算,并将结果缓存起来。这意味着,如果依赖的属性没有变化,下次访问计算属性时,将直接使用缓存的结果,而不会重新执行计算逻辑。
相比之下,函数每次被调用时都会执行其内部的逻辑代码,并返回新的计算结果。因此,函数的结果不会像计算属性那样被缓存。
在实际应用中,我们可以根据是否需要缓存结果这一标准来选择使用计算属性或函数。如果某个值的计算成本较高,且不经常变化,使用计算属性可以提高性能。相反,如果每次访问都需要最新的结果,或者计算非常简单,使用函数可能更合适。
计算属性的赋值
存储属性在 Vue 中主要用于数据的存取,我们可以使用赋值运算来进行属性值的修改。通常,计算属性只用来取值,不会用来存值,因此计算属性默认提供的是取值的方法,我们称之为 get 方法。但是这并不代表计算属性不支持赋值,计算属性也可以通过赋值进行存值操作,存值的方法我们需要手动实现,通常称之为 set 方法。
例如,修改前面编写的代码中的 type 计算属性如下:
// 计算属性:type const type = computed({ // 用来取值的 get 方法 get() { return count.value > 10 ? "大" : "小"; }, // 用来赋值的 set 方法 set(newValue) { if (newValue === "大") { count.value = 11; // 注意:原图 OCR 为 1l,已修正为 11 } else { count.value = 0; // 原图 OCR 为字母 O,已修正为 0 } } });
可以直接使用组件实例进行计算属性 type 的赋值,赋值时会调用我们定义的 set 方法,从而实现对存储属性 count 的修改,示例代码如下:
let instance = Vue.createApp(App).mount("#Application") // 初始值为0 console.log(instance.count) // 初始状态为“小” console.log(instance.type) // 对计算属性进行修改 instance.type = "大" // 打印结果为11 console.log(instance.count)如以上代码所示,在实际使用中,计算属性对于使用它们的人来说是透明的。所谓“透明”,是指使用者不需要了解其背后的工作机制。我们按照普通属性的方式使用计算属性即可,无须关心它们是否由计算属性提供。
但是,需要额外注意的是,如果一个计算属性仅实现了 get 方法而没有实现 set 方法,则它只能用于取值,不能用于赋值。在 Vue 中,这类仅实现 get 方法的计算属性也被称为只读属性。如果我们尝试对一个只读属性进行赋值操作,Vue 会捕获这个操作并发出警告。
例如,如果我们尝试对只读的计算属性 type 赋值,控制台将输出以下异常信息:
[Vue warn]: Write operation failed: computed property "type" is readonly.
属性侦听器
属性侦听是 Vue 框架中一项强大的功能。通过使用侦听器,我们可以方便地监听属性的变化,并据此执行复杂的业务逻辑。举例来说,许多人在使用互联网时都会使用搜索引擎。以百度搜索引擎为例,当我们在搜索框中输入关键字时,网页会自动显示一些推荐词汇供用户选择,如下图所示。

图 1 搜索引擎的推荐词功能
要实现这一功能,就需要对用户的输入行为进行监听,这正是侦听器发挥作用的理想场景。
在 Vue 中,我们可以定义一个侦听器来观察特定数据的变化。当数据变化时,侦听器会自动触发,允许我们执行定义好的逻辑,比如根据用户的输入动态显示搜索建议。
在定义 Vue 组件时,可以通过 watch 函数来定义属性侦听器,首先,创建一个名为 2.watch.html 的文件,在其中编写如下测试代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>属性侦听器</title> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> </head> <body> <div id="Application"> <!-- 定义一个输入框,监听用户输入的内容 --> <input v-model="searchText" /> </div> <script> const { createApp, ref, watch } = Vue; const config = { setup() { const searchText = ref(""); // watch 方法用来定义属性监听器 watch(searchText, (newValue, oldValue) => { console.log(newValue, oldValue); if (newValue.length > 10) { alert("文本太长了"); } }); return { searchText }; } }; createApp(config).mount("#Application"); </script> </body> </html>watch 是 Vue 内置的定义属性监听器的函数,在使用时,watch 函数中需要传入两个参数,其中第 1 个参数为要监听的属性,可以直接对 ref 属性进行监听,第 2 个参数需要设置为一个回调函数,此回调函数会在属性的值发生变化时调用,会将属性变化前后的值作为参数传入。
运行上面的代码,尝试在页面的输入框中输入一些字符,可以看到当输入框中的字符超过 10 个时,就会有警告框弹出,提示输入文本过长,如下图所示:

图 2 属性侦听器应用示例
从一些特性来看,属性侦听器和计算属性有类似的应用场景,使用计算属性的 set 方法也可以实现与上面示例代码类似的功能。