首先Vue2.0双向绑定是运用了object.defineProperty来实现,3.0则是通过Es6中的Proxy和Reflect来实现双向绑定。
2.0中object.defineProperty实现
//监听数组
let ArrayProperty = Array.prototype;
let arrObj = Object.create(ArrayProperty);
['pop', 'push', 'slice'].forEach(method => {
arrObj[method] = function () {
updateView()
ArrayProperty[method].call(this, ...arguments)
}
})
//监听数据
function defineProperty(target, key, value) {
watchData(value)
Object.defineProperties(target, {
get() {
return value
},
set(newVal) {
if (value !== newVal) {
//当给对象属性赋值为对象的时候,需要对赋值的对象进行监听
// watchData(newVal);
updateView()
value = newVal
}
}
})
}
function watchData(val) {
if (typeof val !== 'object' || val === null) {
return val
} else if (Array.isArray(val)) {
//如果是数组对数组的方法进行重写,进行监听,更新视图
val.__proto__ = arrObj
}
for (let key in val) {
defineProperty(val, key, val[key])
}
}
function updateView() {
console.log('触发视图更新')
}
3.0基于Proxy,Reflect实现
const toProxy = new WeakMap() //存放代理后的对象 (防止再次代理)
const toRaw = new WeakMap() //存放的是代理前的对象(已经代理过的对象再次传入去代理)
function reactive(target) {
return createReactive(target)
}
function trigger() {
console.log('触发试图更新')
}
function isObject(target) {
return typeof target === 'object' && target !== null
}
function createReactive(target) {
if (!isObject(target)) {
return target
}
// 如果已经代理过,直接返回代理对象
if(toProxy.get(target)){
return toProxy.get(target)
}
//传入的是代理对象直接返回
if(toRaw.has(target)){
return target
}
let observeHandle = {
get(target, key, receiver) {
// console.log(`getting ${key}!`);
let data = Reflect.get(target, key, receiver)
return isObject(target) ? createReactive(data) : data;
},
set(target, key, value, receiver) {
//触发的是私有属性,更新视图否则屏蔽掉(数组的length)
if (target.hasOwnProperty(key)) {
trigger()
}
return Reflect.set(target, key, value, receiver);
},
deleteProperty(target, key) {
return Reflect.deleteProperty(target, key)
}
}
let observe = new Proxy(target, observeHandle)
toProxy.set(target, observe) //原对象:代理后的结果
toRaw.set(observe,target)
return observe
}
let obj = {
name: 'sc',
arr: [1, 2, 3]
}
let arr = [1, 2, 3]
let proxy = reactive(obj)
//toProxy作用:防止再次代理
proxy = reactive(obj)
proxy = reactive(obj)
proxy = reactive(obj)
//toRaw作用:已经代理过的对象再次传入去代理
proxy = reactive(proxy)
proxy.arr.push(4)
console.log(proxy);
- 2.0和3.0的区别
vue2.0中我们监听数组的变化需要进行拦截导致数组变化的方法(push,slice,reverse,concat等)对方法进行拦截绑定到我们自己定义的方法上,然后更新视图,3.0中Proxy则会自己监听到数组的变化;
在vue2.0中我们一开始拿到数据就进行watchData在里面递归判断属性是不是对象如果是对象的话对属性进行再次监听,3.0则是在我们进行get数据时候才进行递归,提高了性能;
在3.0中我们对数组进行改变时,可能在我们push时候会导致数组size变化再次更新所以我们需要判断是不是自身的属性,其次是判断数组的值有没有变化;
在3.0中我们可能会存在已经代理过的对象再次代理,所以我们使用weakset进行存储,判断在没有代理过的情况下才进行代理