【vue】vue响应式系统不能检测数组变化导致的坑及其解法

在一个组件中,如果某个数据可以通过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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值