# 响应式失效问题:为对象动态添加新属性

常见有 3 种情况,你改动了 vue 的 data,但是 vue 没有重新渲染页面,即,响应式失效:

  1. 为对象添加新属性;
  2. 通过数组下标索引操作数组;
  3. 通过 length 属性为数组扩容或缩容。

# 1. 为对象动态添加新属性

不能使用『为对象不存在的属性赋值』的方式来为对象新增属性,Vue 监测不到这种方式的数据的变动。

const vm = new Vue({
  data() {
    return {
      user : {
        username : 'tommy'
      }
    }
  }
});

// 标准错误用法
vm.user.age = 20;

// Vue 无法发现 user『多』出来一个 age 属性,并且值为 20 。

解决这个问题的常见办法是使用 Vue.set() 代替直接赋值,来为对象『新增』属性:

// 在 vue 内部使用时
Vue.set(this.user, 'age', 20);

// 在 vue 外部使用时
Vue.set(vm.user, 'age', 20);  

# 2. 使用数组下标索引赋值

不能直接使用『下标索引』来设置数组的元素。Vue 监测不到这种方式的数据的变动。

const vm = new Vue({
  el: '#app',
  data: {
    dogs: ['tommy', 'jerry', 'ben']
  },
});

// 标准错误用法
vm.dogs[2] = 'Bob';

// Vue 无法发现 dogs 的第 3 个元素发生了变动。

解决这个问题的常见办法是使用 Vue.set() 代替下标索引方式赋值,来为对象『新增』属性:

// 在 vue 内部使用时
Vue.set(this.dogs, 2, 'Bob');

// 在 vue 外部使用时
Vue.set(vm.dogs, 2, 'Bob');

除了 Vue.set() 方式之外,常见的解决办法还有使用『使用数组的 .splice() 方法』来移除数据中旧元素,并添加新元素,从而间接实现“修改”功能:

// 在 vue 内部使用时
this.dogs.splice(2, 1, 'Bob');

// 在 vue 外部使用时
vm.dogs.splice(2, 1, 'Bob');

# 3. 重置数组长度

JavaScript 的语法特新中有这样一个特性:可以设置一个数组的长度,自动让空元素填充数组至该长度,或者截掉数组的尾部。

具体是填充还是截掉取决于新设置的长度是比数组原长度长还是短。

不过这个特性不能用于处理 data 对象中的数据,因为 Vue 不能检测到该操作对数组的任何更改。

可以用 splice() 方法代替:

vm.dogs.splice(newLength);

不过这一方法只能用于缩短数组,并不能扩展它的长度。

# 4. 响应式失效问题总结

上述响应式失效的 3 种情况,会涉及到 2 个解决问题的方法,其中:

  • Vue.set() 方法,解决情况 1 和 2 ;

  • 数组.splice() 方法,解决情况 2 和 3 。

大家根据情况选择性使用。