詳解Vue3的響應(yīng)式原理解析
Vue2響應(yīng)式原理回顧
// 1.對(duì)象響應(yīng)化:遍歷每個(gè)key,定義getter、setter
// 2.數(shù)組響應(yīng)化:覆蓋數(shù)組原型方法,額外增加通知邏輯
const originalProto = Array.prototype
const arrayProto = 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
}
// 增加數(shù)組類型判斷,若是數(shù)組則覆蓋其原型
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, key, val) {
observe(val) // 解決嵌套對(duì)象問(wèn)題
Object.defineProperty(obj, key, {
get () {
return val
},
set (newVal) {
if (newVal !== val) {
observe(newVal) // 新值是對(duì)象的情況
val = newVal
notifyUpdate()
}
}
})
}
function notifyUpdate () {
console.log('頁(yè)面更新!')
}
vue2響應(yīng)式弊端:
響應(yīng)化過(guò)程需要遞歸遍歷,消耗較大
新加或刪除屬性無(wú)法監(jiān)聽(tīng)
數(shù)組響應(yīng)化需要額外實(shí)現(xiàn)
Map、Set、Class等無(wú)法響應(yīng)式
修改語(yǔ)法有限制
Vue3響應(yīng)式原理剖析
vue3使用ES6的Proxy特性來(lái)解決這些問(wèn)題。
function reactive (obj) {
if (typeof obj !== 'object' && obj != null) {
return obj
}
// Proxy相當(dāng)于在對(duì)象外層加攔截
// http://es6.ruanyifeng.com/#docs/proxy
const observed = new Proxy(obj, {
get (target, key, receiver) {
// Reflect用于執(zhí)行對(duì)象默認(rèn)操作,更規(guī)范、更友好
// Proxy和Object的方法Reflect都有對(duì)應(yīng)
// http://es6.ruanyifeng.com/#docs/reflect
const res = Reflect.get(target, key, receiver)
console.log(`獲取${key}:${res}`)
return res
},
set (target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver)
console.log(`設(shè)置${key}:${value}`)
return res
},
deleteProperty (target, key) {
const res = Reflect.deleteProperty(target, key)
console.log(`刪除${key}:${res}`)
return res
}
})
return observed
}
//代碼測(cè)試
const state = reactive({
foo: 'foo',
bar: { a: 1 }
})
// 1.獲取
state.foo // ok
// 2.設(shè)置已存在屬性
state.foo = 'fooooooo' // ok
// 3.設(shè)置不存在屬性
state.dong = 'dong' // ok
// 4.刪除屬性
delete state.dong // ok
嵌套對(duì)象響應(yīng)式
測(cè)試:嵌套對(duì)象不能響應(yīng)
// 設(shè)置嵌套對(duì)象屬性 react.bar.a = 10 // no ok
添加對(duì)象類型遞歸
// 提取幫助方法
const isObject = val => val !== null && typeof val === 'object'
function reactive (obj) {
//判斷是否對(duì)象
if (!isObject(obj)) {
return obj
}
const observed = new Proxy(obj, {
get (target, key, receiver) {
// ...
// 如果是對(duì)象需要遞歸
return isObject(res) ? reactive(res) : res
},
//...
}
避免重復(fù)代理
重復(fù)代理,比如
reactive(data) // 已代理過(guò)的純對(duì)象
reactive(react) // 代理對(duì)象
解決方式:將之前代理結(jié)果緩存,get時(shí)直接使用
const toProxy = new WeakMap() // 形如obj:observed
const toRaw = new WeakMap() // 形如observed:obj
function reactive (obj) {
//...
// 查找緩存,避免重復(fù)代理
if (toProxy.has(obj)) {
return toProxy.get(obj)
}
if (toRaw.has(obj)) {
return obj
}
const observed = new Proxy(...)
// 緩存代理結(jié)果
toProxy.set(obj, observed)
toRaw.set(observed, obj)
return observed
}
// 測(cè)試效果
console.log(reactive(data) === state)
console.log(reactive(state) === state)
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
前端vue+element使用SM4國(guó)密加密解密的詳細(xì)實(shí)例
國(guó)密即國(guó)家密碼局認(rèn)定的國(guó)產(chǎn)密碼算法,主要有SM1,SM2,SM3,SM4,下面這篇文章主要給大家介紹了關(guān)于前端vue+element使用SM4國(guó)密加密解密的相關(guān)資料,需要的朋友可以參考下2023-03-03
vue基于mint-ui的城市選擇3級(jí)聯(lián)動(dòng)的示例
本篇文章主要介紹了vue基于mint-ui的城市選擇3級(jí)聯(lián)動(dòng)的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
vue使用vue-video-player插件播放視頻的步驟講解
在最近的項(xiàng)目中有一個(gè)視頻播放的功能,在之前的項(xiàng)目中沒(méi)有接觸過(guò)類似的功能,第一次接觸,把具體操作步驟一下,這篇文章主要給大家介紹了關(guān)于vue使用vue-video-player插件播放視頻的相關(guān)資料,需要的朋友可以參考下2022-12-12
在vant 中使用cell組件 定義圖標(biāo)該圖片和位置操作
這篇文章主要介紹了在vant 中使用cell組件 定義圖標(biāo)該圖片和位置操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
vue3封裝el-pagination分頁(yè)組件的完整代碼
這篇文章主要介紹了vue3封裝el-pagination分頁(yè)組件的完整代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-02-02

