在一个组件中,如果某个数据可以通过data
推导出来,那么该数据就没有必要放到data
里,这时可以使用computed
,即计算属性,就像这样:
<template>
<div>
<p>{{message}}</p>
<p>{{reversedMessage}}</p>
</div>
</template>
<script>
export default {
data:function(){
return {
message:"hello world"
}
},
computed:{
reversedMessage:function(){
return this.message.split("").reverse().join("");
}
},
mounted:function(){
setTimeout(() => {
this.message = "have a nice day";
},3000);
}
}
</script>
将message
反转即可计算得到reversedMessage
,且如果message
发生改变,reversedMessage
也会跟着一起变化。
这得益于vue的响应式系统:每个组件实例对应一个Watcher实例,组件渲染过程中将接触过的数据property收集为依赖,一旦依赖项的setter函数被调用,就会通知Watcher,从而使关联的组件重新渲染。
计算属性好用,响应式系统真香,但是,小心坑出没!
//counter.vue
<template>
<div class="counter">
<button class="button" v-on:click="onIncrement">+</button>
<button class="button" v-on:click="onDecrement">-</button>
<span>{{value}}</span>
</div>
</template>
<script>
export default {
props:["value"],
methods:{
onIncrement:function(){
this.$emit("increment");
},
onDecrement:function(){
this.$emit("decrement");
}
}
}
</script>
<style>
.button{
background-color: lavender;
border: 5px solid transparent;
margin: 10px;
}
.button:hover{
cursor:pointer;
}
.button:focus{
outline:none;
}
.counter{
font-size:0.75em;
}
</style>
//counterPanel.vue
<template>
<div class="panel">
<Counter v-for="(value,idx) in values"
v-bind:key="idx"
v-bind:value="value"
v-on:increment="handleIncrement(idx)"
v-on:decrement="handleDecrement(idx)"
/>
<div class="sum">{{sum}}</div>
</div>
</template>
<script>
import Counter from "./counter.vue";
export default {
data:function(){
return {
values:[1,10]
}
},
computed:{
sum:function(){
return this.computeSum()
}
},
methods:{
computeSum:function(){
return this.values.reduce((sum,value) => sum+=value,0)
},
handleIncrement:function(idx){
this.values[idx]++;
// this.values.splice(idx,1,this.values[idx]+1);
// this.$set(this.values,idx,this.values[idx]+1);
},
handleDecrement:function(idx){
this.values[idx]--;
// this.values.splice(idx,1,this.values[idx]-1);
// this.$set(this.values,idx,this.values[idx]-1);
}
},
components:{
Counter
}
}
</script>
<style>
.panel{
font-size:0.75em;
font-weight:bold;
margin:10px;
}
.sum{
border-top:2px solid ;
padding-left:95px;
width:20px;
}
</style>
//index.js
import Vue from "vue";
import CounterPanel from "./counterPanel.vue";
const vm = new Vue({
render:function(createElement){
return createElement(CounterPanel);
}
}).$mount('#root');
瞧,坑来了:不论点击+还是-,计数值、计数和都没有变化。
this.values[idx]++
后,this.values
确实变化了,但vue响应系统检测不到该变化,因此组件也没有重新渲染。
由于Vue响应系统检测不到数组的变化,因此官方文档也很贴心地给出了相应的解决方法,有两种,如下:
- 第一种
this.values.splice(idx,1,this.values[idx]+1)
- 第二种
this.$set(this.values,idx,this.values[idx]+1)