object.defineProperty缺陷:
1、通过数组下标添加元素,无法触发setter。
2、监听数组的push,pop,shift,unshift,splice,sort,reverse方法或者改变length无法触发setter。—>重写数组方法
3、无法检测到对象属性的新增或删除 —>Vue.$set
4、不管data中的对象层级有多深,都需要遍历每个属性,为每一个属性添加object.defineProperty。—>性能问题
Vue2响应式:
// 获取数组的原型 重写数组方法
let oldArrayPrototype = Array.prototype;
// Object.create 创建一个新的对象,使用现有的对象提供新创建的对象的__proto__
let proto = Object.create(oldArrayPrototype);
['push', 'pop', 'shift'].forEach((method) => {
proto[method] = () => {
renderView();
oldArrayPrototype[method].call(this, ...arguments);
}
})
// 3.更新视图
function renderView() {
console.log('更新视图----');
}
// 5.监听处理对象中的每一个属性,只要属性值发生改变 就出发此方法
function reactive(obj, key, val) {
// 7.如果val是个对象就继续监听val 直到val不是对象才行 递归 性能不好,假如对象层级很深性能不好,所以vue3来了proxy
observer(val)
// Object.defineProperty(对象,对象的属性,描述符)
Object.defineProperty(obj, key, {
get() {
return val
},
set(newVal) {
console.log('set', newVal);
observer(newVal)
// 更新视图
renderView()
val = newVal //更新值
}
})
}
// 4.监听对象的变化
function observer(target) {
// 判断target
if(typeof target !== 'object' || target == 'null') {
// number string ....
return target
}
if(Array.isArray(target)) {
// [] 数组的监听
target.__proto__ = proto;
}
// 对象
for (let key in target) {
// 监听每一个属性
reactive(target, key, target[key])
}
}
// 1.定义一个对象
// const obj = {
// name: 'lsx',
// age: [1, 2, 3],
// money: {
// num: 500
// }
// }
const obj = [1,2,3]
observer(obj)
obj.push(4)
// 2.obj.name发生改变 -> 视图更新 对象的key值发生改变就要触发视图更新,所以需要监听这个对象
// obj.name = 'liushuxin'
// 6.改第二层数据 说明value值是个对象
// obj.money.num = 5000
// obj.a = 1
// obj.age.push(1)
proxy更优雅
Vue3响应式:
let proxyArr = reactive([1,2,3])
let proxyObj = reactive({
name: 'lsx',
money: {
num: 100
}
})
// 更新视图
function renderView() {
console.log('更新视图----');
}
// 判断是不是对象
function isObject(target) {
return typeof target === 'object' && target !== null
}
function reactive(target) {
if (!isObject(target)) return target
let observer = new Proxy(target, {
get(target, key, receiver) {
// receiver是代理后的对象
console.log('GET---');
// 多层代理,通过get来判断 vue2是一上来就是遍历每个属性添加object.definProperty(),vue3是有必要才递归
return isObject(target[key]) ? reactive(target[key]) : target[key];
},
// 支持数组
set(target, key, newVal, receiver) {
let oldValue = target[key]
// 判读对象有没有key属性 主要是针对push 方法会触发两次set
if (!target.hasOwnProperty[key]) {
console.log('新增属性');
console.log('SET---');
renderView();
} else if (oldValue !== newVal) {
console.log('更改属性');
console.log('SET---');
renderView();
}
target[key] = newVal;
return target[key];
},
deleteProperty(target, key) {
console.log('delete---');
return Reflect.deleteProperty(target, key);
}
})
return observer;
}
// proxyArr[3] = 4;
//触发两次GET,两次SET 既更改数组下标又更改数组length 会更新两次视图
// proxyArr.push(5);
// 更改更深层次的属性 会触发get, 把获取的对象的代理返回给reactive()函数 说明vue3中并不是遍历每一个属性,而是有必要的时候才递归
proxyObj.money.num = 0;