一、網(wǎng)頁的渲染機制
1. CSSOM 和 DOM
先拋開 JS 不說,瀏覽器收到一張
html頁面時,會從上往下開始生成 DOM(Document Object Model) 樹,如果中途發(fā)現(xiàn)有樣式如<link>引入或者<style>寫法,瀏覽器就會同步生成 CSSOM(CSS Object Model)。當 DOM 和 CSSOM 樹構建好之后,瀏覽器就會開始組合,生成渲染樹(Render-Tree),最后就是渲染(繪制),也就是展示給我們看的內容。這里需要注意兩個詞:構建和渲染。這是兩個階段。

參考:渲染樹構建、布局及繪制 | Web | Google-Developers
2. 阻塞渲染的 CSS
在上文我們提及到,渲染樹是在 CSSOM 和 DOM 構建好之后開始的,也就是說,CSS 和 HTML 是阻塞瀏覽器渲染的,因為如果不完整讀取 CSS 和 HTML ,就無法構建 CSSOM 和 DOM ,也就無法渲染。通常第一個請求就已經(jīng)下載好完整的 HTML 了,也就是說,構建 DOM 是比 CSSOM 快的,因為在解析 HTML 時,才會知道并請求 CSS(CSS 通過
<link>和<style>引入),所以 CSS 會阻塞渲染樹的構建。這也是為什么通常把 CSS 文件的引入放在<head>,早引入,早生成 CSSOM??梢酝ㄟ^媒體查詢來跳過某些 CSS,不納入 CSSOM 的生成過程,加快 CSSOM 構建。
參考:阻塞渲染的 CSS | Web | Google-developers
3. JS 引入
我們知道 JS 是可以修改 DOM 節(jié)點和 DOM 樣式的,所以 JS 既阻塞 DOM、CSSOM 的構建,也阻塞渲染樹的生成。在解析 HTML 中發(fā)現(xiàn)引入了 JS 后,會下載完 JS 并執(zhí)行之后,才會繼續(xù)解析、構建 HTML。因此我們常常把
<script>放到</body>前面,加快網(wǎng)頁生成速度。
參考:使用 JavaScript 添加交互 | Web | Google-developers
4. 完整的瀏覽器渲染過程
實際中,瀏覽器有自己的策略去優(yōu)化頁面的渲染速度,雖然默認情況下。 CSSOM 和 DOM 應該先構建再渲染,但假如網(wǎng)速太慢,瀏覽器有可能先渲染部分內容。至于更復雜的優(yōu)化,這里就不討論了。以下是完整的渲染過程:

二、CSS 和 JS 在網(wǎng)頁中的放置順序是怎樣的?
從上文我們知道,為了加快瀏覽器渲染,我們應該盡早獲取 CSS 和構建 DOM,因此 CSS 通常放在 HTML 的
<head>里面,而 JS 放到</body>之前。
三、解釋白屏和 FOUC
1. 白屏
瀏覽器的渲染需要先生成 DOM 和 CSSOM 樹,假如 CSS 獲取時間過長(比如 CSS 在最后引入),就會導致 CSSOM 生成過久,瀏覽器只能等待,這時候就會出現(xiàn)長時間白屏,直到 DOM 和 CSSOM 構建完成。通常打開頁面都會短暫白屏,因為 DOM 和 CSSOM 構建需要時間。
2. FOUC (flash of unstyled content)
有些瀏覽器(如:FireFox)會先渲染 DOM ,等到 CSSOM 生成了,再重繪渲染,這樣就會看到一部分沒樣式的內容,然后再看到有樣式的。這種分塊分步的渲染。
四、async 和 defer 的作用是什么?有什么區(qū)別
JS 在 HTML 中引入有三種<script> <script async> <script defer>。
1. 直接引入<script>
會阻塞瀏覽器渲染,必須等到 JS 下載并執(zhí)行才繼續(xù)解析 HTML
2. async 方式引入
JS 的下載和 HTML 的解析并行進行,但只要 JS 下載完成,就馬上開始執(zhí)行,同時阻塞 HTML 解析。
3. defer 方式引入
JS 的下載和 HTML 的解析并行進行,但直到 HTML 解析結束,才執(zhí)行 JS。
如圖: