相信有很多小伙伴早已经对vue的双向数据绑定有一定的了解了~,今天我们就来对比和重温一下vue2和vue3实现双向数据绑定的原理以及他们之前的一些细节问题。
首先我们先要明白响应式的底层设计原理:
附上一张图帮助大家加深理解,上面这段文字,大家可以联系平时使用vue时的操作,更好理解
下面我们再来看看在vue2.0和vue3.0里面是分别是如何实现数据相应式的。
vue2.0
// 基本使用
const obj={
name: 'zhangsan',
}
Object.defineProperty(obj, 'name', {
get() {
console.log('触发get')
return value
},
set(newValue) {
if (newValue !== value) {
console.log('触发set')
value = newValue
//updateView()
}
}
})
obj.name // 触发get
obj.name='小小' // 触发set
vue3
vue3.0实现数据的响应式是通过 Proxy。
与Object.defineProperty不同 的是Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象。因为proxy是对整个对象进行代理,所以可以监听对象某个属性值的变化,还可以监听对象属性的新增和删除,而且还可以监听数组。
proxy是ES6的新增的功能,可以用来定义对象中的操作
基本语法:
let pObj = new Proxy(target, handler);
// `target` 代表需要添加代理的对象
// `handler` 用来自定义对象中的操作
proxy是对整个对象进行代理,不关心对象里面具体有什么属性,而defineProperty只能响应首次渲染时候的属性,但是proxy的兼容性不好,IE无法兼容,vue2是兼容到了IE8,但是vue3使用了proxy说明放弃了对IE的兼容考虑。
下面我们分别手写Object.defineProperty,proxy监听一个对象的实现。
Object.defineProperty
// 触发更新视图
function updateView() {
console.log('视图更新')
}
const data = {
name: 'zhangsan',
age: 20,
info: {
address: '北京' // 需要深度监听
},
nums: [10, 20, 30]
}
function defineReactive(target, key, value) {
// 这里为啥要用defineReactive包裹,因为这个函数和get,set形成了闭包,
// 就能保存value的值,不然的话,得定义一个全局变量去保存value
observer(value) //深度监听
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
if (newValue !== value) {
observer(newValue) //这里设置observer是为了 data.age = { num: 1 },设置的值是个对象时进行监听
value = newValue
updateView()
}
}
})
}
// 监听数组
const oldArrayPropoty = Array.prototype
const arrProto = Object.create(oldArrayPropoty)
const arrMethod = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
arrMethod.forEach(methodsName => {
arrProto[methodsName] = function () {
updateView()
oldArrayPropoty[methodsName].call(this, ...arguments)
}
})
function observer(target) {
if (typeof target != 'object' || target == null) {
//不是对象或者数组
return target
}
if (Array.isArray(target)) {
target.__proto__ = arrProto
console.log(target)
}
// 重新定义各个属性(for in 也可以遍历数组)
for (let key in target) {
defineReactive(target, key, target[key])
}
}
observer(data)
// data.name = "小明"
// data.info.address = '上海' // 深度监听
// data.age = { num: 1 }
// data.job='打杂' 对于新增属性是无法监控的
data.nums.push(4)
proxy:
const user = {
name: '小明',
age: 20,
wife: {
name: '小夏',
age: 18
}
}
//把目标对象变成代理对象
//参数1:user----->target目标对象
//参数2:handle---->处理器对象,用来监视数据以及数据操作
const proxyUser = new Proxy(user, {
//获取目标对象的某个属性值
get(target, props) {
console.log('get方法调用了')
return Reflect.get(target, props) //需要用到Reflect将值反射出去,不然是拿不到值的,Reflect配合Proxy使用
},
// 修改目标对象的属性值
set(target, props, value) {
console.log('set方法调用了')
return Reflect.set(target, props,value)
},
//删除目标对象的属性值,为目标对象添加新的属性
deleteProperty(target, props) {
console.log('delete方法调用了')
return Reflect.deleteProperty(target, props)
}
})
console.log(proxyUser.name)
//通过代理对象更新目标对象上的某个属性值
proxyUser.name='大明'
console.log(user)
delete proxyUser.name
console.log(user)
proxyUser.wife.name='小红'
建议想要真正了解 Object.defineProperty 和 proxy 是如何实现数据监听的小伙伴,可以自己手动实现下,对监听的过程加深理解