本文主要包含以下知识点:
- 计算属性
- 计算属性缓存 vs 方法
- 计算属性的 setter
- 数据观察
计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
例如:
...
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
...
在这个地方,模板不再是简单的声明式逻辑。我们必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当我们想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,都应当使用计算属性。
计算属性对应的数据选项为 computed,类似于 data,只不过 data 对应的是一个值,而计算属性对应的是一个方法。在方法里面就可以书写复杂的计算,然后将结果进行返回。
下面是一个计算属性的示例:
<body>
<div id="app">
<p>{{ message }}</p>
<!-- Hello Vue! -->
<p>{{ reverseMess }}</p>
<!-- !euV olleH -->
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const data = {
message: 'Hello Vue!'
}
const vm = new Vue({
el: '#app',
data: data,
computed: {
reverseMess: function () {
return this.message.split('').reverse().join('')
}
}
});
</script>
</body>
效果:计算属性的值依赖于数据,如果数据发生变化,计算属性也会发生变化
计算属性缓存 vs 方法
在 Vue 的实例中,还存在一个数据选项 methods,用来书写方法。
例如上面的例子如果将计算属性修改为方法,代码如下:
<body>
<div id="app">
<p>{{ message }}</p>
<!-- Hello Vue! -->
<p>{{ reverseMess() }}</p>
<!-- !euV olleH -->
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const data = {
message: 'Hello Vue!'
}
const vm = new Vue({
el: '#app',
data: data,
methods: {
reverseMess: function () {
return this.message.split('').reverse().join('')
}
}
});
</script>
</body>
效果:
这个时候,给人的感觉计算属性和方法仿佛是同一个东西。
但是实际上,计算属性只有在它的相关依赖发生改变时才会重新求值。相比而言,只要发生重新渲染,methods 调用总会执行所有函数。
下面我们用一个示例来进行说明:
<body>
<div id="example">
<button v-on:click="a++">A++</button>
<button v-on:click="b++">B++</button>
<!-- computedA 计算属性依赖于数据 a , 当 a 的值发生变化时 , computedA 会重新进行计算 -->
<p>{{ computedA }}</p>
<!-- computedB 计算属性依赖于数据 b , 当 b 的值发生变化时 , computedB 会重新进行计算 -->
<p>{{ computedB }}</p>
<!-- 只要发生重新渲染 , 所有函数就会被执行 -->
<p>{{ methodA() }}</p>
<p>{{ methodB() }}</p>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#example',
data: {
a: 1,
b: 1
},
computed: {
computedA: function () {
console.log('computedA has done');
return this.a;
},
computedB: function () {
console.log('computedB has done');
return this.b;
}
},
methods: {
methodA: function () {
console.log('methodA has done');
return this.a;
},
methodB: function () {
console.log('methodB has done');
return this.b;
}
}
});
</script>
</body>
效果:
那么,什么时候使用计算属性,什么时候使用方法呢?
假设有一个性能开销比较大的操作,它需要遍历一个极大的数组和做大量的计算。这个时候建议使用计算属性来返回结果!即使其他地方发生了局部渲染,也不会影响该计算属性的值,不用再次进行计算。而如果性能开销不大,并且不希望存在缓存,则建议使用方法来返回结果。
计算属性的 setter
一般情况下,计算属性提供一个 getter 即可,也就是模板中直接获取计算属性的值。但是如果涉及到要设置计算属性的值的话,也可以给计算属性设置一个 setter,在 setter 中来对计算属性设置时进行一定的限制。
下面是关于计算属性 setter 的一个示例:
<body>
<!-- 一般来讲 , 我们只会获取计算属性的值 , 如果要设置计算属性的值 , 可以为计算属性设置一个 setter -->
<div id="app">
<!-- fullname 为一个计算属性 -->
<p>你的全名为:{{fullName}}</p>
<!-- xing 和 ming 为数据 -->
<p>你的姓氏为:{{xing}}</p>
<p>你的名字为:{{ming}}</p>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
xing: 'xie',
ming: ' jie'
},
computed: {
// 注意使用 getter 和 setter 的时候,fullName 对应一个对象,而不再是一个函数
// get 或者 set 属性对应的才是一个函数
fullName: {
// getter 用于读取的时候
get: function () {
return this.xing + this.ming;
},
// setter 写入时触发
// 例如在浏览器控制台执行 vm.fullName = 'song ya jing' , 这里就是对计算属性进行了值的设定
set: function (value) {
// 根据新设置的姓名来给 firstName 和 lastName 进行赋值
const newName = value.split(' ');
this.xing = newName[0]; // song
this.ming = ' '; // 首先重置this.ming,注意有一个空格
// 通过for循环将newName剩余的元素拼接到名字里面
for (let i = 1; i < newName.length; i++) {
this.ming += newName[i] + ' '; // ya jing
}
}
}
}
});
</script>
</body>
效果:
数据观察
Vue 提供了一个 watch 数据选项来观察和响应 data 数据的变动。watch 对象的键是需要观察的 data 数据,值对应一个回调函数,回调函数得到的参数为新值和旧值。
示例如下:
<body>
<div id="app">
<button @click="a++">a加1</button>
<p>当前 a 的值为:{{ a }}</p>
<p>{{ message }}</p>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
a: 1,
message: ''
},
watch: {
// 对 data 数据中的 a 进行监控
// 发生改变时 , 新值和旧值会传入回调函数
a: function (val, oldVal) {
this.message = 'a的旧值为' + oldVal + ',新值为' + val;
}
}
})
</script>
</body>
效果:
除了 watch 数据选项以外,实例对象的 $watch 方法也可以监听 data 数据的改变。
示例如下:
<body>
<div id="app">
<button @click="a++">a加1</button>
<p>当前a的值:{{a}}</p>
<p>{{ message }}</p>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
a: 1,
message: ''
}
});
// 实例对象的 $watch 方法也可以监听 data 数据的改变
// 将新值和旧值传入到回调函数里面
vm.$watch('a', function (val, oldVal) {
this.message = 'a的旧值为' + oldVal + ',新值为' + val;
})
</script>
</body>
效果同上。
虽然说 watch 数据选项和 $watch 都可以实现对 data 数据的监听,但是还是有一定的区别。
$watch 方法会返回一个取消观察函数,用来停止触发回调。
示例如下:
<body>
<div id="app">
<button @click="a++">a加1</button>
<p>当前a的值:{{a}}</p>
<p>{{ message }}</p>
</div>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
a: 1,
message: ''
}
});
// 实例对象的 $watch 方法也可以监听 data 数据的改变
// 将新值和旧值传入到回调函数里面
// $watch 会返回一个函数 , 调用该函数可以取消观察
const unwatch = vm.$watch('a', function (val, oldVal) {
if (val === 10) {
unwatch(); // 执行函数取消观察
}
this.message = 'a的旧值为' + oldVal + ',新值为' + val;
})
</script>
</body>
效果:
总结
-
在模板中放入太多的逻辑会让模板过重且难以维护。这个时候可以考虑使用计算属性。
-
计算属性对应的数据选项为 computed,类似于 data,只不过 data 对应的是一个值,而计算属性对应的是一个方法。在方法里面就可以书写复杂的计算,然后将结果进行返回。
-
计算属性与方法的区别在于,计算属性只有在它的相关依赖发生改变时才会重新求值。相比而言,只要发生重新渲染,methods 调用总会执行所有函数。
-
假设有一个性能开销比较大的操作,这个时候建议使用计算属性来返回结果!即使其他地方发生了局部渲染,也不会影响该计算属性的值,不用再次进行计算。而如果性能开销不大,并且不希望存在缓存,则建议使用方法来返回结果。
-
可以给计算属性设置一个 setter,在 setter 中来对计算属性设置时进行一定的限制。
-
Vue 提供了一个 watch 数据选项来观察和响应 data 数据的变动。
-
除了 watch 数据选项以外,实例对象的 $watch 方法也可以监听 data 数据的改变,两者的区别在于后者会返回一个取消观察函数,用来停止触发回调。