朋友問我 vue 響應式怎么實現,我說 Proxy,他沉默了……
Hello,大家好,我是 Sunday。
Reactive 的懶響應性 可能很多同學之前沒有聽說過這個概念。最近一個同學在面試中就被問到了這個問題。所以說,咱們今天就借助這篇文章來看下:在vue3中,什么是 Reactive 的懶響應性?
1.Reactive 的實現原理
Reactive 是基于 Proxy
進行實現的,這個概念很多同學都是知道的。我們可以通過 vue 的源碼來看下這個實現:
圖片
代碼比較復雜,大家可以只看我紅框中的內容。
通過上圖紅框中的代碼,我們可以很清晰的看到在 Reactive 里面,Vue 最終通過 new Proxy
的方式生成了一個 Proxy 的實例對象。 這個 Proxy 的實例就是 reactive()
方法調用時的返回值。
我們可以通過如下偽代碼表示:
function reactive (target) {
return new Proxy(target, {....})
}
const test = reactive({name: '張三'})
在這種情況下,{name: '張三'}
就會被包裝成一個 響應式對象,proxy
通過監聽它的 getter、setter
行為來觸發響應性。
2.Proxy 的問題
Proxy 可以代理對象,這個是沒有問題的。但是:Proxy 只能代理一層對象,而不能代理多層。
什么意思呢?假如當 代理對象 具備層級嵌套的時候,proxy 是不可以代理子層級的。我們可以通過以下代碼來進行測試:
const target = {
name: '張三',
child: {
name: '小張三'
}
}
const p = new Proxy(target, {
set(target, property, value, receiver) {
console.log('觸發了 set');
target[property] = value
return true
},
get(target, property, receiver) {
console.log('觸發了 get');
return target[property]
}
})
p.name = '李四' // 打印:觸發了 set
p.child.name = '小李四' // 打印:觸發了 get。注意:并沒有觸發 child 的 set
在上述代碼中,我們利用 proxy 代理了 target 對象。注意:此時的 target 是存在嵌套的復雜數據類型 child。
當我們執行 p.name = '李四'
時,觸發 set
行為,這是一個很正常的邏輯。但是,當我們執行 p.child.name = '小李四'
時,我們會發現 僅觸發了一個 get(p.child),而沒有觸發 set
行為。
這就證明:對于 Proxy 而言, 它只能代理 一層 的復雜數據類型,而不可以代理多層。
但是,在 Vue 中 多層的 Reactive 對象卻可以實現響應性,那么這是如何做到的呢? 具體的方式就是 Reactive的懶響應性。
3.Reactive的懶響應性
我們觀察 Vue 的源碼,在源碼監聽 proxy getter 行為的地方,存在這樣一段代碼(我把代碼稍微做了一些調整,讓大家看的可以更加清晰):
圖片
核心的代碼就在紅框的位置,其中的 res 大家可以理解為 target[property]
。
而根據 01:Reactive 的實現原理 我們知道 reactive
方法本質上就是生成了一個 Proxy 實例。所以說,這里的代碼核心其實就是 一旦獲取到的屬性是對象,則會為該對象再次執行 reactive
方法。
如果用偽代碼表示,就是下面這樣:
圖片
即:再次通過 Proxy 完成代理,并返回
所以說,所謂的 Reactive的懶響應性 指的就是:Reactive 最初只會為復雜數據類型執行第一層的響應性。如果存在多層的復雜數據類型嵌套時,則會在使用到(getter 行為)該子集時,才會再次為該子集包裝 proxy(執行 reactive 方法)。