搞明白這個,能幫助我們理解開發(fā)中出現(xiàn)的很多問題。
一、我們先來回顧一下vue模板的渲染過程:
(1)執(zhí)行render函數(shù),生成虛擬DOM。
render函數(shù)是根據(jù)render、templete、el這三個選項得來的,優(yōu)先級依次降低。如果有render函數(shù),則直接使用;否則如果有templete選項,則把templete作為模板編譯成render函數(shù);否則把el的outerHTML作為模板編譯成render函數(shù)。單頁面組件中,使用<templete>標簽作為模板編譯成render函數(shù)。
render函數(shù)首次執(zhí)行時,會觸發(fā)模板依賴數(shù)據(jù)的getter,getter會記錄哪個模板用到了自己,同時模板也會記錄自己用到了哪些屬性,這個過程叫依賴收集。
每次模板重新渲染時,模板中的表達式、methods函數(shù)、全局函數(shù)都會重新執(zhí)行。因為render函數(shù)就是個函數(shù),當它執(zhí)行時,里面的表達式和函數(shù)都會跟著執(zhí)行。但這里面有一個例外,就是計算屬性,因為計算屬性會緩存結果,多用計算屬性是個好習慣。
(2)虛擬DOM和數(shù)據(jù)結合,生成真實的DOM。
(3)如果不是首次渲染,生成的虛擬DOM會和原來的進行比較,通過diff算法,計算出來最少的需要更新的DOM。
(4)如果是首次渲染,把DOM掛載到頁面中。如果是重新渲染,則更新DOM。這一步是vue能影響的最后一步。
(5)瀏覽器渲染頁面。這一步是瀏覽器渲染進程操作的,和vue無關。但vue可以在上一步里,計算出DOM的最小更新,來盡量避免回流和重繪。
二、首次渲染和重新渲染的不同
(1)執(zhí)行時機和原理不同
vue從創(chuàng)建實例,到mounted執(zhí)行結束,這一整個生命周期是一個同步的過程。意味著,如果你試圖在created前加async,來讓異步數(shù)據(jù)獲取完成后再進入beforeMount鉤子,是沒用的。
模板的首次渲染,也是一個同步的過程。它是從beforeMount的同步代碼結束開始,同步的生成虛擬DOM、真實DOM、掛載到頁面這一系列操作之后,最后進入mounted鉤子。
但是,從模板首次渲染完成,也就是從進入mounted鉤子開始,之后的渲染邏輯就完全不一樣了,被稱為異步更新隊列。如果說首次渲染是生命周期驅動的,那么重新渲染,就是數(shù)據(jù)的變化驅動的。
當模板所依賴的屬性被重新賦值時,該屬性的setter被觸發(fā)。因為這個屬性記錄了哪些模板用到了自己,它就通知這些模板的Watcher進入微任務隊列。在同一個事件循環(huán)中,如果一個模板所依賴的多個屬性發(fā)生改變,它不會被重復放入到微任務隊列。這就是我們所熟悉的,多次修改數(shù)據(jù),vue只會更新DOM一次。
哪些操作會導致模板的重新渲染呢?從mounted鉤子開始,所有對模板依賴數(shù)據(jù)的更改,都會導致模板的重新渲染。
比如,在mounted中同步修改數(shù)據(jù),在瀏覽器事件中修改數(shù)據(jù),在created中異步獲取數(shù)據(jù)后修改數(shù)據(jù)等。
(2)依賴數(shù)據(jù)不同
要注意的是,我們通常習慣在created里異步獲取數(shù)據(jù),異步數(shù)據(jù)是不會參與到模板的首次渲染中的。當異步數(shù)據(jù)獲取成功后,數(shù)據(jù)的改變會觸發(fā)模板的重新渲染。
模板首次渲染采用的數(shù)據(jù),是prop、data、計算屬性的初始值。更準確的說,是render函數(shù)開始執(zhí)行之前的數(shù)據(jù)。
那么,在beforeCreate、created、beforeMount里對數(shù)據(jù)進行的同步操作,是會作用于模板的首次渲染的。而所有的異步數(shù)據(jù),以及mounted中對數(shù)據(jù)的修改,首次渲染都是用不到的。
無論是首次渲染還是重新渲染,都是基于當前的數(shù)據(jù)。首次渲染時使用的是實例創(chuàng)建時的數(shù)據(jù)狀態(tài),而重新渲染則是基于數(shù)據(jù)變化后的最新狀態(tài)。
三、所以,知道這些有什么用?
(1)在給data中的屬性設初始值時,要考慮健壯性。不要忘了,這些初始值會用于模板的首次渲染。
在開發(fā)中我們經(jīng)常犯這樣的錯誤,就是沒有考慮data初始值對首次渲染的兼容。當然,有個好處是代碼會報錯,幫助我們發(fā)現(xiàn)錯誤。
給我們的啟發(fā)是什么呢?對data賦初值,能多賦,不要少賦。我以前喜歡把所有屬性初始值都設為null,這個習慣很不好,容易報錯。我們應該設置有意義的初始值,至少應該用[]、''、0這些代替null。
(2)開發(fā)中經(jīng)常出現(xiàn),進入頁面后某個元素閃一下沒了。
是因為我們在這個元素使用了v-if,首次使用初始值渲染時是有的,后面異步獲取數(shù)據(jù)后重新計算就沒了,所以會閃一下。
為了避免這個問題,我們應該養(yǎng)成一個習慣,就是元素默認是不顯示的,拿到異步數(shù)據(jù)后再決定是否顯示,這樣就避免了閃一下的情況出現(xiàn)。如果在元素上再加一個進入動畫,就更好了。
還有一個v-cloak指令也是解決閃爍問題的,好像是解決{{}}閃爍問題的,我還沒有用過。