Vue 和 React 的元件更新粒度有什麼區別?

2021-10-07 00:14:24 字數 3924 閱讀 5155

我們都知道 vue 對於響應式屬性的更新,只會精確更新依賴收集的當前元件,而不會遞迴的去更新子元件,這也是它效能強大的原因之一。

舉例來說 這樣的乙個元件:

>

>

}/>

div>

template

>

我們在觸發this.msg = 'hello, changed~'的時候,會觸發元件的更新,檢視的重新渲染。

但是這個元件其實是不會重新渲染的,這是 vue 刻意而為之的。

而 react 在類似的場景下是自頂向下的進行遞迴更新的,也就是說,react 中假如childcomponent裡還有十層巢狀子元素,那麼所有層次都會遞迴的重新render(在不進行手動優化的情況下),這是效能上的災難。(因此,react 創造了fiber,創造了非同步渲染,其實本質上是彌補被自己搞砸了的效能)。

他們能用收集依賴的這套體系嗎?不能,因為他們遵從immutable的設計思想,永遠不在原物件上修改屬性,那麼基於object.definepropertyproxy的響應式依賴收集機制就無從下手了(你永遠返回乙個新的物件,我哪知道你修改了舊物件的哪部分?)

同時,由於沒有響應式的收集依賴,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-compprepatch這個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 屬性 來接收,然後...