天天看点

带你认识Vue的双向数据绑定

版本迭代

1、Vue3之前是使用Object.defineProperty实现的,缺点有以下三点

  • 深度监听需要一次性递归 (遍历每个对象的每个属性,如果对象嵌套很深的话,需要使用递归调用。)
  • 无法监听新增属性/删除属性(Vue.set Vue.delete,未在 data 中定义的属性会报 undefined)
  • 无法原生监听数组,需要特殊处理
// 新增
Vue.set(obj, newkey, newvalue)
vm.$set(obj, newkey, newvalue)
obj = Object.assign({}, obj, {newkey1: newvalue1, newkey2: newvalue2})
// 删除
Vue.delete(obj, key)
vm.$delete(obj, key)

// 数组监听方案
// 修改值
Vue.set(arr, index, newvalue) 
vm.$set(arr, index, newvalue)
arr.splice(index, 1, newvalue)
// 修改长度
arr.splice(newLen)           

2、Vue3使用Proxy实现

  • Proxy可以理解成,在目标对象之前架设一层 "拦截",当外界对该对象访问的时候,都必须经过这层拦截,而Proxy就充当了这种机制,类似于代理的含义,它可以对外界访问对象之前进行过滤和改写该对象。
// 被代理之后返回的对象 = new Proxy(被代理对象,要代理对象的操作)
const obj = new Proxy(target, handler)           
  • handler中常用的对象方法如下:
get(target, propKey, receiver)
set(target, propKey, value, receiver)
has(target, propKey)
construct(target, args):
apply(target, object, args)           

Proxy实现监听

// 创建响应式
function reactive(target {}) { 
   if (typeof target !== 'object' || typeof target == null) {
   	 return target // 不是对象或数组直接返回
   }
   // 代理配置
   const proxyConf = {
		get(targe, key, receiver) {
			// 只处理本身(非原型)的属性
			const ownKeys = Reflect.ownKeys(target)
			if(ownKeys.include(key)) {
				console.log('get', key) // 监听
			}
			const result = Reflect.get(target, key, receiver) // 返回不做处理
			return reactive(result) // 递归调用,这里所做的优化是只在调用到对象深层次的属性时才会触发递归
		}

   	set(target, key, val, receiver) {
   		// 重复的数组,不处理
   		if(val === target[key])  {
   			return true;
   		}
   		const ownKeys = Reflect.ownKeys(target)
		if(ownKeys.include(key)) {
			console.log('set 已有的属性', key) // 监听
		} else {
			console.log('新增的属性', key)
		}
   		const result = Reflect.set(target, key, val, receiver)
   		console.log('set', key, val)
   		return result  // 是否设置成功
   	}

   	deleteProperty(target, key) {
   		const result = Reflect.deleteProperty(target, key)
   		console.log('deleteProperty', key)
   		return result
   	}
    })
    
	// 生成代理 对象
	const observed = new Proxy(target, proxyConf)
	return observed;
}


// 测试数据 
const data = {
   name: 'zhangsan',
   age: 20,
   info: {
   	city: 'beijing',
   	a: {
   		b: {
   			c: {
   				d: e
   			}
   		}
   	}
   }
}           

object.definePropety 的深度监听是一次性就全部监听的,而 proxy 的深度监听是在 get 的时候才去递归的,是一个惰性的,很慢的过程,这就是 proxy 性能的优化

Proxy 优缺点总结

规避了 Object.definedProperty的问题

Proxy 无法兼容所有浏览器,无法进行polyfill

通过proxy直接代理整个对象来实现的,而不是像Object.defineProperty针对某个属性。所以,只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性