vue——计算属性和侦听器
一、计算属性(data中的相关数据)
侦听多个属性时——计算属性 comuted。
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <body> <div id= "computed" > <div> {{msg.split( '' ).reverse().join( '' )}} </div> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > var com = new Vue({ el: "#computed" , data:{ msg: "Hello World" } }) </script> </body> |
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message
的翻转字符串。显示效果如下:
当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,都应当使用计算属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <body> <div id= "computed" > <div> <!--{{msg.split( '' ).reverse().join( '' )}}--> {{reverseStr}} </div> <button @click= "clickHandler" >修改</button> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > var com = new Vue({ el: "#computed" , data:{ msg: "Hello World" }, methods:{ clickHandler(){ this .msg = 'Hello Luffy' } }, computed:{ // 计算属性: watch监听 // 计算属性默认只有getter方法,因此必须return reverseStr(){ return this .msg.split( '' ).reverse().join( '' ); } } }) </script> </body> |
当我点击按钮的时候更改了当前的数据,同时h3和p标签中数据也随时改变。
(1)为什么会这样呢?
因为Vue知道com.currentMsg依赖与com.msg,因此当com.msg发生改变时,所有依赖com.currentMsg的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系。:计算属性的getter函数是没有副作用的,这使它更易于测试和理解。
(2)同样的上面操作,我们不用computed声明的计算属性方法,而仅仅通过methods中声明的方法也能完成上面的效果,那么为什么又要使用computed方法呢?
因为计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要msg还没有发生变化,多次访问currentMsg计算属性会立刻返回之前计算的结果,而不比再次执行函数。同样的。每当触发重新渲染时,调用方法将总会执行函数。
(3)我们为什么需要缓存?
假设我们有一个性能开销比较大的的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
1、计算属性之computed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <body> <div id= "app" > <h4>{{alexDesc}}</h4> <button @click= "clickHandler" >修改</button> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > new Vue({ el: '#app' , template: '' , data(){ return { myName: 'alex' , age:18 } }, methods:{ clickHandler(){ this .myName= 'WUSIR' ; this .age=28; } }, computed:{ alexDesc: function () { var str = `${ this .myName}它的年龄是${ this .age} 岁了可以去大保健了`;<br> // 默认只有getter方法 return str; } } }) </script> </body> |
(1)页面显示效果
(2)按钮点击后显示效果
(3)分析程序
var str = `{this.age} 在实时监听data中声明的数据的变化。
点击事件,对数据属性进行修改,由于计算属性的实时监听,就察觉了数据的修改。
由于计算属性方法用模板插值关联,因此alexDesc函数的返回值就直接显示在模板中了。
2、计算属性的setter方法
计算属性默认只有 getter ,不过在需要时也可以提供一个 setter 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <body> <div id= "app" > <h4>{{alexDesc}}</h4> <button @click= "clickHandler" >修改</button> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > new Vue({ el: '#app' , template: '' , data(){ return { myName: 'alex' , age:18 } }, methods:{ clickHandler(){ console.log( this .alexDesc); this .alexDesc = 'ALEX IS SB!!!' ; } }, computed:{ alexDesc:{ // setter set: function (newValue) { console.log(newValue); this .myName = newValue; }, // getter get: function (){ var str = `${ this .myName}它的年龄是${ this .age} 岁了可以去大保健了`; return str; } } } }) </script> </body> |
(1)计算属性setter固定编写套路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | computed:{ alexDesc:{ // setter set: function (newValue) { console.log(newValue); this .myName = newValue; }, // getter get: function (){ var str = `${ this .myName}它的年龄是${ this .age} 岁了可以去大保健了`; return str; } } } |
(2)显示效果
(3)点击事件传值给计算属性setter方法
3、进一步理解setter用途
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <body> <div id= "app" > <input type= "text" v-model= "alexDesc" > <h4>{{alexDesc}}</h4> <!--<button @click= "clickHandler" >修改</button>--> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > new Vue({ el: '#app' , template: '' , data(){ return { myName: '' , } }, computed:{ alexDesc:{ // setter,给myName赋新值 set: function (newValue) { this .myName = newValue; }, // getter,实时监听myName属性的变化 get: function (){ return this .myName; } } } }) </script> </body> |
在input表单中输入信息,使用v-model进行双向数据绑定,使用setter给myName赋新值。getter监听myName属性的变化,并将值显示在 <h4>{{ alexDesc }} </h4>。
4、计算属性案例——音乐播放器
(1)构建音乐播放器页面
<audio>标签是 HTML5 的新标签。<audio>标签定义声音,比如音乐或其他音频流。
- autoplay属性:如果出现该属性,则音频在就绪后马上播放。
- src属性:要播放音频的URL。
- controls属性:如果出现该属性,则向用户显示控件,比如播放控件。
- loop属性:如果出现该属性,则每当音频结束时重新开始播放。
- muted属性:规定视频输出应该被静音。
- preload属性:如果出现该属性,则音频在页面加载时进行加载,并预备播放。如果使用 "autoplay",则忽略该属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <body> <div id= "music" > <audio src= "../static/那吾克热-水滴石穿.mp3" controls= "" autoplay= "" ></audio> <ul> <li v- for = "(item, index) in musics" > <h3>{{item.id}}--歌曲为:{{item.name}}</h3> <p>歌手:{{item.author}}</p> </li> </ul> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > var musicData = [{ id:1, name: "那吾克热-水滴石穿" , author: "那吾克热" , songSrc: '../static/那吾克热-水滴石穿.mp3' }, { id:2, name: "Inna-10 Minutes" , author: "Inna" , songSrc: '../static/10 Minutes.mp3' }, { id:3, name: "Devotion-My_Prayer" , author: "Devotion" , songSrc: '../static/My_Prayer.mp3' } ]; new Vue({ el: '#music' , data(){ return { musics:musicData } }, template: '' }); </script> </body> |
显示效果:
(2)计算属性监听切换播放歌曲
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <body> <div id= "music" > <audio v-bind:src= "currentSrc" controls= "" autoplay= "" ></audio> <ul> <li v- for = "(item, index) in musics" @click= "clickHandler(index)" > <!--给每个li绑定点击事件--> <h3>{{item.id}}--歌曲为:{{item.name}}</h3> <p>歌手:{{item.author}}</p> </li> </ul> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > var musicData = [{ id:1, name: "那吾克热-水滴石穿" , author: "那吾克热" , songSrc: '../static/那吾克热-水滴石穿.mp3' }, { id:2, name: "Inna-10 Minutes" , author: "Inna" , songSrc: '../static/10 Minutes.mp3' }, { id:3, name: "Devotion-My_Prayer" , author: "Devotion" , songSrc: '../static/My_Prayer.mp3' } ]; new Vue({ el: '#music' , data(){ return { musics:musicData, musicSrc: '../static/那吾克热-水滴石穿.mp3' } }, methods:{ clickHandler(index){ // 声明点击事件 // alert(index); this .musicSrc = this .musics[index].songSrc; // 获取数组musics中对应index的歌曲资源 } }, computed:{ currentSrc(){ // 实时监听musicSrc return this .musicSrc; } }, template: '' }); </script> </body> |
(3)不再监听musicSrc,改为监听musics数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <body> <div id= "music" > <audio v-bind:src= "currentSrc" controls= "" autoplay= "" ></audio> <ul> <li v- for = "(item, index) in musics" @click= "clickHandler(index)" > <!--给每个li绑定点击事件--> <h3>{{item.id}}--歌曲为:{{item.name}}</h3> <p>歌手:{{item.author}}</p> </li> </ul> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > var musicData = [{ id:1, name: "那吾克热-水滴石穿" , author: "那吾克热" , songSrc: '../static/那吾克热-水滴石穿.mp3' }, { id:2, name: "Inna-10 Minutes" , author: "Inna" , songSrc: '../static/10 Minutes.mp3' }, { id:3, name: "Devotion-My_Prayer" , author: "Devotion" , songSrc: '../static/My_Prayer.mp3' } ]; new Vue({ el: '#music' , data(){ return { musics:musicData, currentIndex:0 // musicSrc:'../static/那吾克热-水滴石穿.mp3' } }, methods:{ clickHandler(index){ // 声明点击事件 // alert(index); this .currentIndex = index; // 点击事件修改index } }, computed:{ currentSrc(){ // 实时监听了两个属性:musics、currentIndex return this .musics[ this .currentIndex].songSrc // 修改的index会导致获取的歌曲资源不同 } }, template: '' }); </script> </body> |
(4)添加点选切换样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <head> <meta charset= "UTF-8" > <title>Title</title> <style type= "text/css" > *{ padding: 0; margin: 0; } ul{ list-style: none; } ul li{ margin: 30px 20px; padding: 10px; } ul li.active{ background-color: #20FFFF; } </style> </head> <body> <div id= "music" > <audio v-bind:src= "currentSrc" controls= "" autoplay= "" ></audio> <ul> <li v- for = "(item, index) in musics" @click= "clickHandler(index)" : class = "{active:currentIndex==index}" > <!--给当前歌曲li添加 class =active--> <h3>{{item.id}}--歌曲为:{{item.name}}</h3> <p>歌手:{{item.author}}</p> </li> </ul> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > var musicData = [{ id:1, name: "那吾克热-水滴石穿" , author: "那吾克热" , songSrc: '../static/那吾克热-水滴石穿.mp3' }, { id:2, name: "Inna-10 Minutes" , author: "Inna" , songSrc: '../static/10 Minutes.mp3' }, { id:3, name: "Devotion-My_Prayer" , author: "Devotion" , songSrc: '../static/My_Prayer.mp3' } ]; new Vue({ el: '#music' , data(){ return { musics:musicData, currentIndex:0 // musicSrc:'../static/那吾克热-水滴石穿.mp3' } }, methods:{ clickHandler(index){ // 声明点击事件 // alert(index); this .currentIndex = index; // 点击事件修改index } }, computed:{ currentSrc(){ // 实时监听了两个属性:musics、currentIndex return this .musics[ this .currentIndex].songSrc // 修改的index会导致获取的歌曲资源不同 } }, template: '' }); </script> </body> |
点击事件的时候修改currentIndex,自己的li标签监听currentIndex,修改为对应的标签,添加class=active,显示效果如下所示:
二、侦听器(watch)
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。因此Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
1、简单侦听器示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <body> <div id= "app" > <input type= "text" v-model= "myName" > <h3>{{myName}}</h3> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > new Vue({ el: '#app' , template: '' , data(){ return { myName: '' } }, watch:{ // 检测单个属性 命令式 myName: function (value) { // 通过watch来监听myName属性 console.log(value); if (value === 'alex' ){ console.log(value+ "是sb" ); } } } }) </script> </body> |
通过watch来监听myName属性的变化,当属性值为alex时,控制台打印alex是sb。
2、结合其他属性一起检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <body> <div id= "app" > <input type= "text" v-model= "myName" > <h3>{{myName}}</h3> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > new Vue({ el: '#app' , template: '' , data(){ return { myName: '' , firstName: 'wuSir' } }, watch:{ // 检测单个属性 命令式的 myName: function (value) { // 通过watch来监听myName属性 console.log(value); if (value === 'alex' ){ console.log(value + " " + this .firstName + "是sb" ); } } } }) </script> </body> |
显示效果:
三、计算属性 vs 侦听属性 对比
侦听器:侦听的是单个属性;
计算属性:侦听多个属性;
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch
回调。
1、命令式的 watch 回调
添加按钮,并给按钮绑定事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <body> <div id= "app" > <input type= "text" v-model= "myName" > <h3>{{myName}}</h3> <button @click= "clickHandler" >修改</button> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > new Vue({ el: '#app' , template: '' , data(){ return { myName: '' , firstName: 'wuSir' } }, methods:{ clickHandler(){ this .myName = '日天' ; } }, watch:{ // 检测单个属性 命令式的 myName: function (value) { // 通过watch来监听myName属性 console.log(value); if (value === 'alex' ){ console.log(value + " " + this .firstName + "是sb" ); } } } }) </script> </body> |
点击按钮显示效果如下:
可以看到控制台也输出“日天”,这个输出的语句是来自:console.log(value);
如果将事件改为:this.myName = 'alex'; 则还会触发检测的if判断,显示效果如下所示:
2、计算属性版本
上面的代码是命令式且重复的,将它与计算属性的版本进行比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <body> <div id= "app" > <input type= "text" v-model= "myName" > <h3>{{myName}}</h3> <button @click= "clickHandler" >修改</button> </div> <script type= "text/javascript" src= "./vue.js" ></script> <script type= "text/javascript" > var vm = new Vue({ el: '#app' , data(){ return { myName: '' , firstName: 'wuSir' , } }, methods:{ clickHandler(){ this .myName = 'alex' ; } }, computed: { fullName: function (value) { // 计算属性的名字不能与data中属性同名 if (value === 'alex' ) { console.log(value + " " + this .firstName + "是sb!" ) } } } }) </script> </body> |
修改为计算属性的版本,明显比上面命令式的要好得多。显示效果如下所示:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术