# 前端渲染過程的二三事
本文不會介紹整個前端渲染過程的步驟,只是記錄最近閱讀的文章的些許思考和感悟。([文章地址一(系列)](https://developers.google.cn/web/fundamentals/performance/critical-rendering-path/),[文章地址二](https://calendar.perfplanet.com/2012/deciphering-the-critical-rendering-path/))
但願你們在閱讀這篇文章以前能將上述文章仔細瀏覽一篇,由於本文所述基本是基於其內容。
## Navigation Timing API和瀏覽器加載事件
Navigation Timing API:可經過打印(performance)查看;
瀏覽器加載事件:domContentLoaded,onload
上圖整理了Navigation Timing API中的一些事件和瀏覽器加載事件的發生順序。
這些事件的含義是什麼呢?咱們直接引用[文章地址一(系列)](https://developers.google.cn/web/fundamentals/performance/critical-rendering-path/measure-crp)的介紹:
- domLoading:這是整個過程的起始時間戳,瀏覽器即將開始解析第一批收到的 HTML 文檔字節。
- domInteractive:表示瀏覽器完成對全部 HTML 的解析而且 DOM 構建完成的時間點。
- domContentLoaded:表示 DOM 準備就緒而且沒有樣式表阻止 JavaScript 執行的時間點,這意味着如今咱們能夠構建渲染樹了。
- 許多 JavaScript 框架都會等待此事件發生後,纔開始執行它們本身的邏輯。所以,瀏覽器會捕獲 EventStart 和 EventEnd 時間戳,讓咱們可以追蹤執行所花費的時間。
- domComplete:顧名思義,全部處理完成,而且網頁上的全部資源(圖像等)都已下載完畢,也就是說,加載轉環已中止旋轉。
- loadEvent:做爲每一個網頁加載的最後一步,瀏覽器會觸發 onload 事件,以便觸發額外的應用邏輯。
看了上述事件的介紹,大體瞭解了每一個事件所表明的含義,但也帶着少量疑惑:
1. HTML解析是分批進行的嗎?爲何要分批進行?
2. domContentLoaded事件結束後能夠構建渲染樹了,是否意味着CSSOM樹構建也在該事件以前就已完成?
3. domContentLoaded事件表示DOM準備就緒而且沒有樣式表阻止 JavaScript執行的時間點,但是JavaScript執行是在構建DOM樹以前,那這是否是意味着該事件在DOM樹構建完成後就執行了?如果這與domInteractive事件有什麼區別呢?
首先看第一個問題,咱們直接用chrome控制檯的performance來看效果。
上圖中,上方的Network的藍條是HTML的下載時間,下方箭頭所指是HTML的解析時間。
從中咱們能夠得出HTML解析的確是分批進行,而且解析並不須要等HTML徹底下載完。那爲何要分批進行呢?這個問題我找了許多資料也沒有得出結論,因而本身從中思考。假設不是分批進行,那實現會有兩種狀況:1.等HTML所有下載完,再一塊兒解析;2.每下載必定量的HTML就將其放入解析器等待排隊解析。前者首先確定被排除,若HTML很大,會影響首屏加載速度,後者按理速度更快,或許其實現的難度以及可能會帶來的一些問題而沒有采用?這個問題思考了良久,感受從理論上是可行的,但從技術角度,因爲本身這方面的知識實在薄弱,因此也沒法得知其實現的過程是否存在技術瓶頸。
第二個問題:CSSOM樹構建是否在domContentLoaded事件以前?
話很少說咱們本身實踐。
首先我先建立一個HTML文件並寫入少量標籤,再建立一個CSS並於HTML引入,在CSS文件中寫入大量內容(本身實踐時寫了5W多行)。而後用performance查看。
上圖每一個箭頭表明的含義:
1. HTML解析結束時間
2. domContentLoaded加載時間
3. CSS文件下載完的時間
4. CSS文件解析的時間
從這個步驟以及其所處時間,咱們能夠清晰的得出,該結論不許確。那麼該做者爲何會得出該結論呢,是他犯錯了嗎?我隨後發現他在這篇文章下面還寫了一句「domContentLoaded通常表示DOM和CSSOM均準備就緒的時間點」。那麼這句話意味着大部分的時候CSSOM樹的構建是在domContentLoaded事件以前。但是這個大部分又指的是什麼狀況,這又涉及到另外一個知識點「DOM樹,CSSOM樹,JS的三角關係」,構造DOM樹時碰見JS會先解析執行JS,而在解析執行JS時遇到CSSOM,又會先構造CSSOM樹,這個過程稍後會具體說明。那麼如今咱們能夠明白這個問題的關鍵所在了,由於在大部分頁面中是擁有JS的,而因爲其解析順序,那麼在domContentLoaded事件以前一定已經成功構造CSSOM樹。
第三個問題:domInteractive與domContentLoaded的區別是什麼呢?這兩個事件中間是否還會進行其餘操做?
咱們都知道script標籤有defer和async兩個屬性。有了這兩個屬性,瀏覽器就會加一個進程下載JS。那麼下載完的執行時間點是在何時?其中async會在JS下載完後立馬執行,也正是這個緣由,會致使JS的執行順序不必定按標籤的從上至下,而是按照下載完的時間。那麼defer屬性的執行時間呢,我想你們應該都能猜到了,它的執行時間點的確就是在上述的兩個事件之間,那麼咱們也就得知這兩個事件的區別所在。
## 三大樹
咱們都知道前端渲染有三大樹:DOM樹,CSSOM樹,RENDER樹。那麼這三大樹的構造時間和上述的事件執行時間的順序又是怎樣的呢。
其中DOM樹和RENDER樹所在位置實際上是顯而易見的,而且在以前內容也已經指出。DOM樹在domInteractive事件以前,RENDER樹在domContentLoaded事件以後。可是CSSOM樹就難以捉摸,其與DOM樹的關係,徹底受到是否擁有JS影響。
在肯定CSSOM樹所處位置前,咱們先肯定一個上面提到的概念:構造DOM樹時碰見JS會先解析執行JS,而在解析執行JS時遇到CSSOM,又會先構造CSSOM樹。官方給出的緣由是,JS會使用document.write而改變DOM樹,因此構造DOM樹時碰到JS會先執行JS;而JS在執行時,須要查找CSS,因此執行JS時,碰到構造CSSOM樹,會先構造CSSOM樹。可是這裏有一點奇怪的時,JS也能夠經過創造Link標籤的方式改變CSSOM樹,因此我的感受官方的這種解釋有點牽強。不過官方的解釋雖然不能讓人徹底信服,但這執行順序是不會有錯的。
接下來咱們分別經過有無JS兩大類確認CSSOM樹所處位置:
1. 無JS的狀況
因爲DOM樹和CSSOM樹是並行解析的狀況,因此這兩個樹構建完成的順序徹底沒法固定,只由它們本身自己大小有關。所以CSSOM樹構建完成的時間既可能在domInteractive以前,可能在domContentLoaded以後,也可能在這兩事件之間。
2. 有JS的狀況
這裏咱們先假設CSS文件很小,在沒還解析到JS時就已完成解析,那麼這種狀況其實一定發生在domInteractive以前,由於都還沒解析到JS,說明DOM樹一定沒有構建完成。那麼再假設CSS文件很大,而後中途遇到JS文件,這時候JS文件發如今構造CSSOM樹,其就會等待CSSOM樹構建完成後再解析執行JS,因此這種狀況CSSOM也一定在domInteractive以前。
可是有2個有意思的狀況:
(1) 若是咱們動態建立link標籤並添加到html中,那麼又會發生什麼呢?若JS還沒解析執行完,那麼會中止JS而去解析CSS,若JS已執行完那麼CSSOM樹其實也不是一定在domInteractive以前。(固然這可能已經不算初次渲染構建)
(2) link標籤放到JS後面又會發生什麼呢?這種狀況我發現無論我怎麼嘗試,CSSOM樹一定在DOM樹構建以前構建,但其又在JS執行完成後。若是有興趣的同窗能夠查查是爲何。
## 總結
其實前端渲染是一個很龐大的知識點,而且其涉及的周邊知識也及其龐大,本文只是對其中一個小知識點作了思考和實踐。最後要說的一點是,以上內容,純屬我的看法,若有不當,請多指教。