手撕Vue2和Vue3响应式原理

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;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值