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