转自:https://www.cnblogs.com/Super-scarlett/p/11132034.html
https://www.jianshu.com/p/5fe073e36baf
https://www.cnblogs.com/xieli26/p/10108703.html
最近使用vue开发时,在一个函数中使用for循环,改变了页面中的数组,在函数中查看是修改成功的,但是页面中没有动态刷新。
在Vue的官方文档有提到这样一个注意事项:
数组变更检测注意事项:
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:
vm.items[indexOfItem] = newValue
vm.items.length = newLength
举个例子:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
也就是说,直接设置数组的某一项的值,虽然改变了数组的值,但视图上显示的仍为数组之前的值,数据的响应失效了。出现这种现象的根本原因是什么呢?
首先我们先来了解vue数据响应的原理。官方文档的解释:
当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
也就是说当改变data中属性的值时会触发其相应setter的调用,从而实现响应的操作。但getter和setter是有局限性的。我们先来看下面的这个例子:
var person = {};
Object.defineProperties( person, {
age: {
defaultValue: 11,
get: function () {
return this.defaultValue;
},
set: function (val) {
this.defaultValue = val;
console.log("触发了set")
}
}
});
// 修改属性的值时能够触发set
person.age = 12 // 触发了set
->触发了set
->12
person.age
->12
// 将属性的值设置为一个数组,当通过索引值修改数组的某一项或使用数组的某些方法修改数组时不能触发set
person.age = [2,3,4] // 触发了set
->触发了set
->(3) [2, 3, 4]
person.age[2] = 5 // 未触发set
->5
person.age
->(3) [2, 3, 5]
person.age.push(5) // 未触发set
->4
person.age
->(4) [2, 3, 4, 5]
// 将属性的值设置为一个对象,当修改对象中某属性的值时无法触发set
person.age = { first: 1 }
->触发了set
->{first: 1}
person.age.first = 2 // 未触发set
->2
通过上述例子可以观察得出:
当该属性的值为一个数组时,通过索引修改数组某一项的值或使用数组的某些方法修改数组并不能触发set;当属性的值为一对象时,直接修改对象中属性的值时也无法触发set。
为了解决当你利用索引直接设置一个数组项问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue
相同的效果,同时也将在响应式系统内触发状态更新:
// 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)
对象变更检测注意事项:
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value)
方法向嵌套对象添加响应式属性。例如,对于:
var vm = new Vue({
data: {
userProfile: {
name: 'Anika'
}
}
})
当修改对象的属性或为对象添加属性时,应该使用以下方法:
Vue.set(vm.userProfile, 'age', 27)
或者
vm.$set(vm.userProfile, 'age', 27)
有时你可能需要为已有对象赋值多个新属性,比如使用 Object.assign()
或 _.extend()
。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:
Object.assign(vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
你应该这样做:
vm.userProfile = Object.assign({}, vm.userProfile, {
age: 27,
favoriteColor: 'Vue Green'
})
由于数据响应原理机制, Vue 不允许动态添加根级响应式属性,所以你必须在初始化实例前声明所有可能用到的根级响应式属性,且为这些属性都设一个初值,哪怕只是一个空值。
回归正题,我项目中遇到的这个问题,解决方法:
1. 运用this.$forceUpdate()强制刷新。
2. 使用vm.$set(vm.items, indexOfItem, newValue)
eg. vm.$set(vm.dataList[i], picUrl, 'data:image/jpg;base64,' + response.data)
export default {
data() {
return {
dataList:[],
};
},
methods: {
getData() {
var _this = this;
var dataList = [];
dataList = response.data.data;
for(var i=0;i