0%

Vue 如何通过索引修改数组的值

Vue通过索引修改数组值

最近在写通过 data.slide 数组动态生成列表的时候遇到了一个问题:

1
2
3
4
5
6
7
8
<div class="title-level-one"  v-for="(item, index) of slide" :key="index" @click="slideClick(index)">
{{item.name}}
<div v-show="arr[index]">
<div class="title-level-two" v-for="(subtitle, index2) of item.data" :key="index2">
{{subtitle}}
</div>
</div>
</div>

这样写没有问题,的确会根据 slide 和 slide.data 的数组去生成对应数量的列表.但是此时我想对列表做一个隐藏和显示 CSS 时, 遇到了问题:

1
2
3
4
5
6
7
8
slideClick (index) {
this.arr[index] = !this.arr[index])
console.log(`arr`, this.arr)
}
},
created: function () {
this.arr = Array(this.slide.length).fill(false)
}

此时我打印 arr,发现 this.arr 的确是变了,但是 Vue 并没有响应,也就是 v-show 并没有变化.一开始我以为是 this.arr 在 fill 的时候被附了一个新对象,但是仔细一想就发现不是,在初始化的时候列表已经被隐藏了,所以肯定是因为某个原因 Vue 没有响应到this.arr[index] = !this.arr[index]).

再仔细查了一遍官方教程以后发现了这么一段话:

由于 JavaScript 的限制,Vue 不能检测以下变动的数组:

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

举个例子:

1
2
3
4
5
6
7
8
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})

vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:

1
2
3
4
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用 splice
vm.items.splice(newLength)

原因是:

因为vue的底层是通过Object.defineProperty来实现数据监听更新视图的,通过索引设置数组的值不能被这个方法监听到,所以无法触发更新

所以我这里虽然修改了 this.arr 的值,但并没被 Vue 响应到.

我用数组的 splice 方法改写成如下:

1
this.arr.splice(index, 1, !this.arr[index])

这样就可以了

查官方教程的时候还看到了: 对象更改检测注意事项

还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除

1
2
3
4
5
6
7
8
9
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 现在是响应式的

vm.b = 2
// `vm.b` 不是响应式的

对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。例如,对于:

1
2
3
4
5
6
7
8
9

var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})

你可以添加一个新的 age 属性到嵌套的 userProfile 对象:
Vue.set(vm.userProfile, ‘age’, 27)
你还可以使用 vm.$set 实例方法,它只是全局 Vue.set 的别名:
vm.$set(vm.userProfile, ‘age’, 27)
有时你可能需要为已有对象赋予多个新属性,比如使用 Object.assign()_.extend()。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:

1
2
3
4
Object.assign(vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})

你应该这样做:

1
2
3
4
5
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})