Vue | 面试——vue如何解决新增对象的属性为响应式 && 数组的变化也为响应式

100 阅读1分钟

我们都知道vue是用Object.defineProperty实现响应式的。但是Object.defineProperty实现响应式的时候有两个弊端:
1. 无法检测到对象属性的新增或删除 image.png 2. 不能监听数组的变化

当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如:vm.items.length = newLength

image.png

一.对象:

接下来看一个例子,如果我们再Vue中想要给对象中添加一个属性,是什么样的结果呢?

<template>
  <div id="app">
      <ul>
          <li v-for="val in obj">{{val}}</li>
      </ul>
      <button @click="addProperty">给对象添加属性</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
      return{
         obj:{
             name:'zs',
             age:18,
             sex:'男'
         }
      }
  },
  methods:{
     addProperty(){
         this.obj.school='北京大学';
         console.log(this.obj)

     }
  }
}
</script>

image.png

从结果可以看到,本身obj对象中已经有新增的属性了,但是这个新增的属性并没有更新到试图中, 如果我们想要把对象中新增的属性页更新到试图中,那么就要用到 $set

image.png

如果把以上代码改成$set添加

<template>
  <div id="app">
      <ul>
          <li v-for="val in obj">{{val}}</li>
      </ul>
      <button @click="addProperty">给对象添加属性</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
      return{
         obj:{
             name:'zs',
             age:18,
             sex:'男'
         }
          
      }
  },
  methods:{
     addProperty(){
         this.$set(this.obj,'school','北京大学')
         console.log(this.obj)

     }
  }
}
</script>

从输出的结果我们可以看出,新增的属性school不仅可以在对象中已经被添加上了,而且视图页更新了

image.png

二、对于数组

<template>
  <div id="app">
      <ul>
          <li v-for="(val,index) in arr" :key="index">{{val}}</li>
      </ul>
      <button @click="addProperty">给数组添加值</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
      return{
         obj:{
             name:'zs',
             age:18,
             sex:'男'
         },
         arr:['a','b','c']
          
      }
  },
  methods:{
     addProperty(){
         //使用下标修改数组中的某个个值
         this.arr[0]='你好';
         //修改数组的长度
         this.arr.length=10;
         console.log(this.arr)

     }
  }
}
</script>

image.png

从结果我们可以看到,控制台中的数组已经变化了,但试图中的数组并没有更新

image.png

接下来我们用$set

<template>
  <div id="app">
      <ul>
          <li v-for="(val,index) in arr" :key="index">{{val}}</li>
      </ul>
      <button @click="addProperty">给数组添加值</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
      return{
         obj:{
             name:'zs',
             age:18,
             sex:'男'
         },
         arr:['a','b','c'] 
      }
  },
  methods:{
     addProperty(){
         //使用下标修改数组中的某个个值
         this.$set(this.arr,0,'你好')
          //修改数组的长度
         this.arr.splice(1)
         console.log(this.arr)
     }
  }
}
</script>

image.png

我们发现结果也是可以改变的

但是有一个问题欢迎各位路过的大神帮忙解答:

官网上说 当你修改数组的长度时,例如:vm.items.length = newLength

可以使用vm.items.splice(newLength)

但实际中我们发现用splice如果想要把数组长度变长,splice是不生效的,如果把数组长度变短是生效的,这是为什么呢?????