我們都知道 vue 對於響應式屬性的更新,只會精確更新依賴收集的當前元件,而不會遞迴的去更新子元件,這也是它效能強大的原因之一。
舉例來說 這樣的乙個元件:
>
>
}/>
div>
template
>
我們在觸發this.msg = 'hello, changed~'
的時候,會觸發元件的更新,檢視的重新渲染。
但是這個元件其實是不會重新渲染的,這是 vue 刻意而為之的。
而 react 在類似的場景下是自頂向下的進行遞迴更新的,也就是說,react 中假如childcomponent
裡還有十層巢狀子元素,那麼所有層次都會遞迴的重新render(在不進行手動優化的情況下),這是效能上的災難。(因此,react 創造了fiber
,創造了非同步渲染,其實本質上是彌補被自己搞砸了的效能)。
他們能用收集依賴的這套體系嗎?不能,因為他們遵從immutable
的設計思想,永遠不在原物件上修改屬性,那麼基於object.defineproperty
或proxy
的響應式依賴收集機制就無從下手了(你永遠返回乙個新的物件,我哪知道你修改了舊物件的哪部分?)
同時,由於沒有響應式的收集依賴,react 只能遞迴的把所有子元件都重新render
一遍,然後再通過diff演算法
決定要更新哪部分的檢視,這個遞迴的過程叫做reconciler
,聽起來很酷,但是效能很災難。
那麼,vue 這種精確的更新是怎麼做的呢?其實每個元件都有自己的渲染watcher
,它掌管了當前元件的檢視更新,但是並不會掌管childcomponent
的更新。
那麼有同學可能要問了,如果我們把msg
這個響應式元素通過props傳給childcomponent
,此時它怎麼更新呢?
其實,msg
在傳給子元件的時候,會被儲存在子元件例項的_props
上,並且被定義成了響應式屬性,而子元件的模板中對於msg
的訪問其實是被**到_props.msg
上去的,所以自然也能精確的收集到依賴,只要childcomponent
在模板裡也讀取了這個屬性。
這裡要注意乙個細節,其實父元件發生重渲染的時候,是會重新計算子元件的props
的,具體是在updatechildcomponent
中的:
// update props
if(propsdata && vm.$options.props)
toggleobserving
(true
)// keep a copy of raw propsdata
vm.$options.propsdata = propsdata
}
那麼,由於上面注釋標明的那段**,msg
的變化通過_props
的響應式能力,也讓子元件重新渲染了,到目前為止,都只有真的用到了msg
的元件被重新渲染了。
這也是為什麼我們說:vue 的響應式更新粒度是精細到元件級別的。
正如官網api
文件中所說:
vm.$forceupdate
:迫使 vue 例項重新渲染。注意它僅僅影響例項本身和插入插槽內容的子元件,而不是所有子元件。—— vm-forceupdate文件
我們需要知道乙個小知識點,vm.$forceupdate
本質上就是觸發了渲染watcher
的重新執行,和你去修改乙個響應式的屬性觸發更新的原理是一模一樣的,它只是幫你呼叫了vm._watcher.update()
(只是提供給你了乙個便捷的api,在設計模式中叫做門面模式)
注意這裡也提到了乙個細節,也就是 插入插槽內容的子元件:
舉例來說
假設我們有父元件parent-comp
:
>
>
>
}span
>
slot-comp
>
div>
子元件slot-comp
:
>
>
slot
>
div>
元件中含有slot
的更新 ,是屬於比較特殊的場景。
這裡的msg
屬性在進行依賴收集的時候,收集到的是arent-comp
的`渲染watcher。(至於為什麼,你看一下它所在的渲染上下文就懂了。)
那麼我們想象msg
此時更新了,
>
>
>
}span
>
slot-comp
>
div>
這個元件在更新的時候,遇到了乙個子元件slot-comp
,按照vue
的精確更新策略來說,子元件是不會重新渲染的。
但是在原始碼內部,它做了乙個判斷,在執行slot-comp
的prepatch
這個hook的時候,會執行updatechildcomponent
邏輯,在這個函式內部會發現它有slot
元素。
prepatch
(oldvnode: mountedcomponentvnode, vnode: mountedcomponentvnode)
,
在updatechildcomponent
內部
const haschildren =!!
(// 這玩意就是 slot 元素
renderchildren ||
// has new static slots
vm.$options._renderchildren ||
// has old static slots
parentvnode.data.scopedslots ||
// has new scoped slots
vm.$scopedslots !== emptyobject // has old scoped slots
)
然後下面走乙個判斷
if
(haschildren)
這裡呼叫了slot-comp
元件vm例項上的$forceupdate
,那麼它所觸發的渲染watcher
就是屬於slot-comp
的渲染watcher
了。
總結來說,這次msg
的更新不光觸發了parent-comp
的重渲染,也進一步的觸發了擁有slot的子元件slot-comp
的重渲染。
它也只是觸發了兩層渲染,如果slot-comp
內部又渲染了其他元件slot-child
,那麼此時它是不會進行遞迴更新的。(只要slot-child
元件不要再有slot
了)。
比起 react 的遞迴更新,是不是還是好上很多呢?
React元件更新混亂
原因 元件key值的設定不規範導致。解決辦法 給資料項設定唯一的id。使用id給元件設定key值,而不要使用,隨機的,或者按順序設定key值。問題情況 渲染元件 data 當期資料 i 當期下標 delete 刪除選框開啟 setdelete 設定刪除函式 that.state.data.map f...
React 元件更新優化
3.react.memo 4.usememo shouldcomponentupdate和react.purecomponent是類元件中的優化方式,而react.memo是函式元件中的優化方式。class componentdiffpure extends purecomponent change...
Vue元件通訊和React元件通訊的方法
父子通訊 在vue中,父子通訊,就是資料存放在父元件中,然後子元件根據父元件的資料變化來變化,那麼就要想辦法將父元件的資料傳遞給子元件。在父元件中使用子元件,然後在子元件標籤上通過 屬性 屬性值 來寫乙個自定義屬性,就可以將父元件的屬性傳遞給子元件,然後在子元件中通過 props 屬性 來接收,然後...