天天看點

帶你認識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針對某個屬性。是以,隻需做一層代理就可以監聽同級結構下的所有屬性變化,包括新增屬性和删除屬性