vue3.0 升級內容
全部用TS重寫的(響應式、vdom、模本編譯)
性能提升,減少代碼量
會調整部分API
Proxy重寫響應式
vue2.x 馬上要過時了嗎
vue3.0從正式發佈到推廣,還需要一段時間
vue2.x應用範圍廣,有大量項目需要維護升級
proxy存在兼容性問題,且不能ployfill
社區熱門知識點:Proxy重寫響應式講解
回顧vue2.*的響應式原理 [object.defindeProperty]
object.defindeProperty缺點:
- 深度監聽需要一次性遞歸
- 無法監聽新增屬性/刪除屬性(vue.set/vue.delete)
- 無法原生監聽數組,需要特殊處理
vue3_Proxy實現響應式原理
前置知識
Proxy ES6語法 對象用於定義基本操作的自定義行爲(如屬性查找、賦值、枚舉、函數調用等)。
Proxy 可以理解成, 在目標對象之前架設一層“ 攔截”, 外界對該對象的訪問, 都必須先通過這層攔截, 因此提供了一種機制, 可以對外界的訪問進行過濾和改寫。
語法:const p = new Proxy(target, handler)
target 要使用 Proxy 包裝的目標對象(可以是任何類型的對象,包括原生數組,函數,甚至另一個代理)
handler 一個通常以函數作爲屬性的對象, 各屬性中的函數分別定義了在執行各種操作時代理 p 的行爲。
handler.get() 方法用於攔截對象的讀取屬性操作。
handler.set() 方法是設置屬性值操作的捕獲器。
handler.deleteProperty() 方法用於攔截對對象屬性的 delete 操作
Reflect 是一個內置的對象,它提供攔截 JavaScript 操作的方法這些方法與proxy handlers的方法相同。
Reflect.get(target, propertyKey[, receiver])
Reflect.deleteProperty(target, propertyKey)
Reflect.set(target, propertyKey, value[, receiver])
target: 需要取值的目標對象; key: 需要獲取的值的鍵值;value::設置的值。
如果target對象中指定了getter, receiver則爲getter調用時的this值
1. Proxy對數據攔截監聽的基本使用
1、用Proxy將目標對象data進行包裝攔截處理如下:
const proxyData = new Proxy(data, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
console.log('get', key) //監聽
return result //返回結果
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
console.log('set', key, value) //set age 30
return result //是否設置成功
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key) //delete property name
return result //是否刪除成功
},
})
2、定義data爲對象,並對data對象進行操作時
const data = {
name: 'lili',
age: 20,
}
// 對象操作
proxyData.age // get操作 : get age
proxyData.age = 30 // set操作 : set age 30
proxyData.sex = "女" // set操作 : set sex 女
delete proxyData.name // 刪除操作 : delete property name
不足:
在對象設置屬性時,無法確定是新增屬性還是原有屬性;
3、定義data爲數組,並對data數組進行操作
const data =['a','b','c']
proxyData.push('d')
// get push push()方法觸發
// get length //獲取數組長度
// set 3 d //設置值
// set length 4 設置數組長度
不足:
給數組添加元素時,沒必監聽 原型的屬性,如push(),只需要監聽本身(非原型)的屬性,
set 3 d,set length 4 爲重複處理同一個數據,set length 4多餘
2. Proxy對數據攔截監聽使用的優化
針對以上問題,對Proxy對數據攔截監聽使用進行優化
1.在對象設置屬性時,無法確定是新增屬性還是原有屬性
在set方法中判斷
if (ownKeys.includes(key)) {
console.log('已有的 key') //監聽
} else {
console.log('新增的 key')
}
2、在監聽屬性時,只監聽本身(非原型)的屬性
在get方法中,判斷如果是自身的屬性,才進行監聽
// Reflect.ownKeys()方法可以返回包含Symbol屬性在內的自有屬性。
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('get',key)//監聽
}
3、重複的數據不處理
在 set方法中,重複數據不處理
const oldVal=target[key]
if (value === oldVal) {
return true
}
以上問題,Proxy對數據攔截監聽使用的優化後:
const proxyData = new Proxy(data, {
// target:目標對象、 key:被捕獲的屬性名、receiver:Proxy或者繼承Proxy的對象
get(target, key, receiver) {
// 只監聽 處理本身(非原型)的屬性
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('get', key) //監聽
}
const result = Reflect.get(target, key, receiver)
return result //返回結果
},
// value 新屬性值。
set(target, key, value, receiver) {
if (ownKeys.includes(key)) {
console.log('已有的 key') //監聽
} else {
console.log('新增的 key')
}
// 重複的數據不處理
if (value === target[key]) {
return true
}
const result = Reflect.set(target, key, value, receiver)
console.log('set', key, value) //set age 30
return result //是否設置成功
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key) //delete property name
return result //是否刪除成功
},
})
定義data爲數組,並對data數組進行操作時
const data =['a','b','c']
proxyData.push('d')
// get length //獲取數組長度
// set 3 d //設置值
實現了只保留了對自身屬性的監聽,重複數據沒有重複設置
3. Proxy實現響應式
實現思路:
① 創建響應式方法reactive(data),該方法可以傳入需要處理的數據對象data
函數內邏輯:
② 判斷 data 是否爲 對象或者數組,不是直接返回
③ 創建 Proxy 代理對象,Proxy對象中傳入data
Proxy 代理對象中的方法配置:
④ 在get()方法對數據的進行監聽:只監聽 處理本身(非原型)的屬性;在返回結果中採用遞歸調用reactive(),實現對數據的深度監聽
⑤ 在 set() 方法中進行數據的新增和更新:判斷是否是新增數據;重複的數據不處理;
⑥ 在 deleteProperty() 方法中對數據進行刪除操作;
實例:
⑦ 定義數據data,傳入響應式方法中,返回的值proxyData爲實現響應式的可操作數據
// 創建響應式
function reactive(target = {}) {
if (typeof target != 'object' || target == null) {
// 不是對象或者數組,則返回
return target
}
// 代理配置 生成代理對象
return observed = new Proxy(target, {
// target:目標對象、 key:被捕獲的屬性名、receiver:Proxy或者繼承Proxy的對象
get(target, key, receiver) {
// 只監聽 處理本身(非原型)的屬性 ,如push()
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('get', key) //監聽
}
const result = Reflect.get(target, key, receiver)
// return result //返回結果
// 深度監聽
// 性能如何提升的?
return reactive(result) //遞歸get處理 實現深度監聽
},
// value 新屬性值。
set(target, key, value, receiver) {
// 判斷是否是新增屬性
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('已有的 key') //監聽
} else {
console.log('新增的 key')
}
// 重複的數據不處理
const oldVal = target[key]
if (value === oldVal) {
return true
}
const result = Reflect.set(target, key, value, receiver)
console.log('set', key, value) //set age 30
return result //是否設置成功
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key) //delete property name
console.log('result', result) //result true
return result //是否刪除成功
},
})
}
// 測試數據
const data = {
name: 'lili',
age: 20,
info: {
city: "beijing"
}
}
const proxyData = reactive(data)