CSS和JS在網(wǎng)頁中的放置順序
- 網(wǎng)站加載的整個(gè)完整過程:
- 首先瀏覽器從服務(wù)器接收到html代碼,然后開始解析html
- 構(gòu)建DOM樹(自html頂向下進(jìn)行構(gòu)建),并且在同時(shí)構(gòu)建渲染樹
- 遇到JS文件加載執(zhí)行,將阻塞DOM樹的構(gòu)建;遇到CSS文件,將阻塞渲染樹的構(gòu)建
(script標(biāo)簽中的defer屬性:構(gòu)建DOM樹的過程和js文件的加載異步(并行)進(jìn)行,但是js文件執(zhí)行需要在DOM樹構(gòu)建完成之后。
script標(biāo)簽中的async屬性:構(gòu)建DOM樹、渲染樹的過程和js文件的加載和執(zhí)行異步(并行)進(jìn)行)
- 為何要注意CSS和JS在網(wǎng)頁中的放置順序
- 若CSS樣式放在底部,對于IE瀏覽器,在點(diǎn)擊鏈接、輸入U(xiǎn)RL、使用書簽進(jìn)入等情況下,會(huì)逐步加載無樣式的內(nèi)容,等CSS加載后頁面突然會(huì)展現(xiàn)樣式,影響閱讀。
- 若JS放在頂部(
<head>標(biāo)簽等),會(huì)禁用并發(fā),阻止其他內(nèi)容的下載,影響頁面加載的速度;如果js文件比較大,算法也比較復(fù)雜的話,影響更大。
白屏和FOUC
- 在寫HTML代碼時(shí),我們都是將CSS文件的引入位置放在頭部(
<head>標(biāo)簽內(nèi)部),將js文件的引入位置放在底部(</body>前面)。 - 不同的瀏覽器對于CSS和HTML的處理方式不同,有的是等待CSS加載完成之后,對HTML元素進(jìn)行渲染和展示(白屏問題)。有的是先對HTML元素進(jìn)行展示,然后等待CSS加載完成之后重新對樣式進(jìn)行修改(FOUC無樣式內(nèi)容閃爍)
- 如果把CSS樣式放在底部,對于IE瀏覽器,在某些場景下(新窗口打開,刷新等)頁面會(huì)出現(xiàn)白屏,而不是內(nèi)容逐步展現(xiàn),如果使用 @import標(biāo)簽,即使 CSS 放入 link, 并且放在頭部,也可能出現(xiàn)白屏。
- 如果把CSS樣式放在底部,對于IE瀏覽器,在某些場景下(點(diǎn)擊鏈接,輸入U(xiǎn)RL,使用書簽進(jìn)入等),會(huì)出現(xiàn) FOUC 現(xiàn)象(逐步加載無樣式的內(nèi)容,等CSS加載后頁面突然展現(xiàn)樣式).對于 Firefox 會(huì)一直表現(xiàn)出 FOUC 。
- 如果把js文件放在頭部,腳本會(huì)阻塞后面內(nèi)容的呈現(xiàn),腳本會(huì)阻塞其后組件的下載,出現(xiàn)白屏問題。
async與defer的作用和區(qū)別
作用
<script src="script.js"></script>
沒有defer或async,瀏覽器會(huì)立即加載并執(zhí)行指定的腳本(“立即”指的是在渲染該script標(biāo)簽之下的文檔之前),也就是說不等后續(xù)載入的文檔元素,讀到就加載并執(zhí)行。
<script async src="script.js"></script>
有async,加載和渲染后續(xù)文檔元素的過程將和script.js的加載與執(zhí)行并行進(jìn)行(異步)。
<script defer src="script.js"></script>
有defer,加載后續(xù)文檔元素的過程和script.js的加載并行進(jìn)行(異步),但script.js的執(zhí)行要在所有元素解析完成之后,DOMContentLoaded事件觸發(fā)之前完成。區(qū)別

藍(lán)色線代表網(wǎng)絡(luò)讀取,紅色線代表執(zhí)行時(shí)間,這倆都是針對腳本的;綠色線代表 HTML 解析。
defer 和 async 在網(wǎng)絡(luò)讀?。ㄏ螺d)這塊兒是一樣的,都是異步的(相較于 HTML 解析),它倆的差別在于腳本下載完之后何時(shí)執(zhí)行。
defer:腳本延遲到文檔解析和顯示后執(zhí)行,有順序。
async:不保證順序。
網(wǎng)頁的渲染機(jī)制
- 構(gòu)建DOM樹(DOM tree):從上到下解析HTML文檔生成DOM節(jié)點(diǎn)樹(DOM tree),也叫內(nèi)容樹(content tree);
- 構(gòu)建CSSOM(CSS Object Model)樹:加載解析樣式生成CSSOM樹;
- 執(zhí)行JavaScript:加載并執(zhí)行JavaScript代碼(包括內(nèi)聯(lián)代碼或外聯(lián)JavaScript文件);
- 構(gòu)建渲染樹(render tree):根據(jù)DOM樹和CSSOM樹,生成渲染樹(render tree);
渲染樹:按順序展示在屏幕上的一系列矩形,這些矩形帶有字體,顏色和尺寸等視覺屬性。 - 布局(layout):根據(jù)渲染樹將節(jié)點(diǎn)樹的每一個(gè)節(jié)點(diǎn)布局在屏幕上的正確位置;
- 繪制(painting):遍歷渲染樹繪制所有節(jié)點(diǎn),為每一個(gè)節(jié)點(diǎn)適用對應(yīng)的樣式,這一過程是通過UI后端模塊完成;

為了更友好的用戶體驗(yàn),瀏覽器會(huì)盡可能快的展現(xiàn)內(nèi)容,而不會(huì)等到文檔所有內(nèi)容到達(dá)才開始解析和構(gòu)建/布局渲染樹,而是每次處理一部分,并展現(xiàn)在屏幕上,這也是為什么我們經(jīng)??梢钥吹巾撁婕虞d的時(shí)候內(nèi)容是從上到下一點(diǎn)一點(diǎn)展現(xiàn)的。
Webkit渲染引擎流程如下圖:
