在一个函数中,改变了data中的数据,在函数中查看是修改成功的,但是页面中并没有刷新。
尤大的解释:由于性能的代价与获得的用户体验不成正比,故vue2.0的实现中放弃了这个特性。在vue3.0中,使用proxy
替代了Object.defineProperty()
数组变更检测注意事项:
由于JS的限制,以下两种情况,vue不能检测以下数组的变动
- 当利用索引直接修改数组的某一项时
- 直接修改数组的长度时
示例:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
也就是说,直接设置数组的某一项时,虽然改变了数组的值,但视图上显示的仍然为数组之前的值,数据的响应式失效了。
vue的数据劫持是利用Object.defineProperty()
的get,set
方法,但是get,set
是有限制的。示例:
var person = {};
Object.defineProperty( 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。
解决方法:
-
Vue.set
:Vue.set(vm.items, indexOfItem, newValue)
-
vm.$set(vm.items, indexOfItem, newValue)
也可以使用vm.$set
实例方法,该方法是全局方法Vue.set
的一个别名 vm.items.splice(indexOfItem, 1, newValue)
- 运用
this.$forceUpdate()
强制刷新。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<ul>
<li v-for='(item,index) in list' :key='index'>{{item}}</li>
</ul>
<button @click='change'>按钮</button>
</div>
</body>
</html>
<script src="./lib/vue-2.6.10.js"></script>
<script>
var vm = new Vue({
el:'#app',
data:{
list:[
1,2,3,4,5
]
},
methods: {
change(){
this.list[0] = 2
// Vue.set(vm.list,0,2)
vm.list.splice(0,1,2)
// this.$forceUpdate()
console.log(this.list);
}
},
})
</script>
proxy
-
Object.define()
只能对属性进行劫持,需要遍历对象的每个属性,若属性值也是对象,则需要深度遍历。而Proxy
直接代理对象,不需要遍历操作。 - 新增属性时,需要重新遍历对象,对其新增属性在使用
Object.define()
进行劫持。
参考链接: