Vue2中响应式原则缺陷
- Vue内部会递归的去循环vue中的data属性,会给每个属性都增加getter和setter,当属性值变化时会更新视图。
- 重写了数组中的方法,当调用数组方法时会触发更新,也会对数组中的数据(对象类型)进行了监控
<!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">
<p>{{address}}</p>
<p>{{name.a}}</p>
<p>{{arr}}</p>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.8/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data() {
return {
name: {
n: 'hs'
},
arr: [1, 2, 3],
address:{}
}
}
})
// 一、不存在的属性,如果新增的话不能渲染视图,内部会采用defineProperty重新定义属性(getter setter)
vm.name.a = 90
// 第一种解决方式,通过用展开运算符获得原来的对象,再额外添加属性,从而触发更新
vm.name = {
...vm.name,
a: 91
}
// 第二种方式,通过$set触发内部更新
vm.$set(vm.name, 'a', 92)
vm.name.a = 93
// 所以数据应该尽量少些嵌套关系
// 二、数组只能通过改变数组的七个方法来实现更新视图,不能使用索引、长度
vm.arr.push(4)
// Vue额外提供的API
vm.$set(vm.arr, 0, -1); // 修改数组内部使用的是splice方法
vm.$set(vm.address, 'number', '6-301'); // 新增属性通过内部会将属性定义成响应式数据
vm.$delete(vm.arr, 0); // 删除索引,属性
</script>
</body>
</html>
通过以上两点可以发现Vue中的缺陷:
- 对象默认只监控自带的属性,新增的属性响应式不生效 (层级过深,性能差)
- 数组通过索引进行修改 或者 修改数组的长度,响应式不生效
Vue额外提供的API:
vm.$set(vm.arr,0,100); // 修改数组内部使用的是splice方法
vm.$set(vm.address,'number','6-301'); // 新增属性通过内部会将属性定义成响应式数据
vm.$delete(vm.arr,0); // 删除索引,属性
为了解决以上问题,Vue3.0使用Proxy来解决,不需要以上来就进行递归(性能好,但是兼容性差)
let obj = {
name: {name: 'jw'},
arr: ['吃', '喝', '玩']
}
let handler = {
get(target,key){
if(typeof target[key] === 'object' && target[key] !== null){
return new Proxy(target[key],handler);
}
return Reflect.get(target,key);
},
set(target,key,value){
let oldValue = target[key];
if(!oldValue){
console.log('新增属性')
}else if(oldValue !== value){
console.log('修改属性')
}
return Reflect.set(target,key,value);
}
}
let proxy = new Proxy(obj,handler);
代理 get、set方法,可以实现懒代理。并且兼容数组索引和长度变化