詳解Vue3的響應式原理解析
阿新 • • 發佈:2021-12-16
目錄
- 2響應式原理回顧
- Vue3響應式原理剖析
- 巢狀物件響應式
- 避免重複代理
- 總結
Vue2響應式原理回顧
// 1.物件響應化:遍歷每個key,定義getter、setter
// 2.陣列響應化:覆蓋陣列原型方法,額外增加通知邏輯
const originalProto = Array.prototype
const arrayProtowww.cppcns.com = Object.create(originalProto)
;['push','pop','shift','unshift','splice','reverse','sort'].forEach(
method => {
arrayProto[method] = function () {
originalProto[method].apply(this,arguments)
notifyUpdate()
}
}
)
function observe (obj) {
if (typeof obj !== 'object' || obj == null) {
return
}
// 增加陣列型別判斷,若是陣列則覆蓋其原型
if (Array.isArray(obj)) {
Object.setPrototypeOf(obj,arrayProto)
} else {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
defineReactive(obj,key,obj[key])
}
}
}
function defineReactive (obj,val) {
observe(val) // 解決巢狀物件問題
Object.defineProperty(obj,{
get () {
return val
},set (newVal) {
if (newVal !== val) {
observe(newVal) // 新值是物件的情況
val = newVal
notifyUpdate()
}
}
})
}
function notifyUpdate () {
console.log('頁面更新!')
}
vue2響應式弊端:
響應化過程需要遞迴遍歷,消耗較大
新加或刪除屬性無法監聽
陣列響應化需要額外實現
Map、Set、Class等無法響應式
修改語法有限制
Vue3響應式原理剖析
vue3使用ES6的Proxy特性來解決這些問題。
function reactive (obj) {
if (typeof obj !== 'object' && obj != null) {
return obj
}
// Proxy相當於在物件外層加攔截
// http://es6.ruanyifeng.com/#docs/proxy
consMjyNhwCgBKt observed = new Proxy(obj,{
get (target,receiver) {
// Reflect用於執行物件預設操作,更規範、更友好
// Proxy和Object的方法Reflect都有對應
// http://es6.ruanyifeng.com/#docs/reflect
const res = Reflect.get(target,receiver)
console.log(`獲取${key}:${res}`)
return res
},set (target,value,receiver) {
const res = Reflect.set(target,receiver)
console.log(`設定${key}:${value}`)
return res
},deleteProperty (target,key) {
const res = Reflect.deleteProperty(target,key)
console.log(`刪除${key}:${res}`)
return res
}
})
return observed
}
//程式碼測試
const state = reactive({
foo: 'foo',bar: { a: 1 }
})
// 1.獲取
state.foo // ok
// 2.設定已存在屬性
state.foo = 'fooooooo' // ok
// 3.設定不存在屬性
state.dong = 'dong' // ok
// 4.刪除屬性
delete state.dong // ok
巢狀物件響應式
測試:巢狀物件不能響應
// 設定巢狀物件屬性 react.bar.a = 10 // no ok
新增物件型別遞迴
// 提取幫助方法 const isObject = val => val !== null && typeof val === 'object' function reactive (obj) { //判斷是否物件 if (!isObject(obj)) { return obj } const observed = new Proxy(obj,{ get (target,receiver) { // ... // 如果是物件需要遞迴 MjyNhwCgBK return isObject(res) ? reactive(res) : res },//... }
避免重複代理
重複代理,比如
reactive(data) // 已代理過的純物件
reactive(react) // 代理物件
解決方式:將之前代理結果快取,get時直接使用
const toProxy = new WeakMap() // 形如obj:observed const toRaw = new WeakMap() // 形如observed:obj function reactive (obj) { //... // 查詢快取,避免重複代理 if (toProxy.has(obj)) { return toProxy.get(obj) } if (toRaw.has(obj)) { return obj } const observed = new Proxy(...) // 快取代理結果 toProxy.set(obj,observed) toRaw.set(observed,obj) return observed } // 測試效果 console.log(reawww.cppcns.comctive(data) === state) console.log(reactive(state) === state)
總結
本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注我們的更多內容!