计算属性和侦听器都可以监听到data区数据的变化,当数据变化时可以触发方法的调用,从而在方法内部可以进行相应的逻辑处理。
计算属性的语法格式是:computed: {}
侦听器的语法格式是:watch: {}
一、计算属性
计算属性一般是为了简化模板中的内容,让模板中内容尽可能简洁。如果我们将太多的逻辑写在模板中,模板将会变得难以维护。
下面举一反例,我们将字符串逆序的逻辑写在模板中。
<div id="app"> <div id="example"> {{ message.split('').reverse().join('') }} div>div><script> let vm = new Vue({ el: '#app', data: { message: 'Hello World' } })script>
如果我们将字符串逆序的逻辑抽取到计算属性中,模板会更清晰,改造如下:
<div id="app"> <div id="example"> {{ resMsg }} div>div><script> let vm = new Vue({ el: '#app', data: { message: 'Hello World' }, computed: { resMsg() { return this.message.split('').reverse().join('') } } })script>
我们在computed中声明了一个计算属性resMsg,通过这个属性来接收变量message逆序之后的值。
火眼金睛的你应该发现了resMsg更像一个方法,而不像一个属性。这是因为计算属性的简写方式导致,不是它的原始样子,原始样子如下:
computed: { resMsg: { get() { return this.message.split('').reverse().join('') } } }
计算属性默认只有get方法,当只有get方法时,就可以进行简写。
当然,如果有需要,我们可以为计算属性提供它的set方法,set方法在计算属性的值被修改时自动调用。这个特性一般在前后端交互时,数据格式不一致时会用到。比如时间、金钱等等在前后端的格式就通常不一样。
我们以钱为例来说明一下set方法的应用场景。
通常情况下,后端为了避开浮点数存储不精确的问题,会以分为单位进行整数存储。然而,前端在展示的时候,通常是以元为单位,这时两个单位就不一致,它们相差100倍。
现在前端通过接口调用,拿到的钱是100分,赋值给cents变量,该值要转化成元在页面上展示,我们就可以声明一个yuan的计算属性去实现。当计算属性yuan被修改时,对应的分又要赋值到cents变量上,这时候set方法就派上用场了,代码如下
<div id="app"> 分:{{cents}} <hr> 元:{{yuan}} <hr> 改变元:<input type="text" v-model="yuan">div><script> let vm = new Vue({ el: '#app', data: { cents: 100 }, computed: { yuan: { get: function () { return this.cents / 100; }, set: function (newVal) { this.cents = newVal * 100 } } } })script>
我们就通过set方法自动被调用的特性,在方法内部,将元转变成分,并赋值给cents变量。
通常情况下,我们只会用到get方法,所以简写形式居多,但不要把它当做方法。它跟方法是不一样的,方法的每次调用,方法内部逻辑都会执行一遍。计算属性却不会,它具备基于依赖的缓存能力。下面例子来证实这个现象。
<div id="app"> <div>{{reverseMsg}}div> <div>{{reverseMsg}}div> <hr> <div>{{reverseStr(msg)}}div> <div>{{reverseStr(msg)}}div>div><script> let vm = new Vue({ el: '#app', data: { msg: 'Hello' }, computed: { reverseMsg() { console.log('computed') return this.msg.split('').reverse().join('') } }, methods: { reverseStr() { console.log('method') return this.msg.split('').reverse().join('') } } })script>
上面代码中,计算属性在模板中使用了两次,方法调用也使用了两次,我们查看打印发现
方法中的打印语句执行了两次,而计算属性中的打印语句却只执行了一次。这就验证了,计算属性的简写形式虽然长得跟方法一模一样,但它确实不是方法。
二、侦听器
侦听器也可以监听到data中的数据变化,当data中某个变量的数据发生变化时,对应的侦听器方法就会被执行,侦听器的方法名必须跟被侦听的变量名一样。
<div id="app"> <label> 名: <input type="text" v-model="firstName"> label> <br> <label> 姓: <input type="text" v-model="lastName"> label> <br> {{fullName}}div><script> let vm = new Vue({ el: '#app', data: { firstName: 'Jim', lastName: 'Green', fullName: 'Jim Green' }, watch: { firstName(val) { console.log('firstName has changed') this.fullName = val + ' ' + this.lastName }, lastName(val) { console.log('lastName has changed') this.fullName = this.firstName + ' ' + val } } })script>
firstName(val)就是监听firstName变化时被调用的函数,lastName(val)就是监听lastName变化时被调用的函数。运行页面如下:
当我们修改firstName的值时,firstName(val)方法就会被调用,并且新值会传递给val变量,我们利用新值可以去更新fullName变量。
侦听器的应用场景通常是数据变化时需要执行异步操作或开销较大的操作。
总结
计算属性和侦听器有相似之处,又有不同之处。相似之处是,它们都可以监听到数据的变化,从而执行自己的处理逻辑。但是它们的应用场景是不一样的,平常开发中,计算属性使用频率更高。