1、CSS和JS在網(wǎng)頁(yè)中的放置順序是怎樣的?
(1)CSS
對(duì)于谷歌瀏覽器和Safari放在head里或body里都一樣。因?yàn)樗窃谌康臉邮奖硗耆虞d下來之后才開始渲染頁(yè)面,將內(nèi)容呈現(xiàn)在頁(yè)面上。
對(duì)于Firefox,head標(biāo)簽中的<link rel="stylesheet">行為與Chrome/Safari中完全一致,這些link標(biāo)簽全部加載完成之前,頁(yè)面上不顯示內(nèi)容。而body標(biāo)簽中的<link rel="stylesheet">則不阻塞任何內(nèi)容顯示,會(huì)出現(xiàn)FOUC無樣式內(nèi)容閃爍。
對(duì)于IE/Edge。未加載完成的<link rel="stylesheet">標(biāo)簽只阻塞其后面的HTML內(nèi)容顯示,而對(duì)其前面的HTML內(nèi)容則不阻塞,所以如果將CSS放在前面head里則和Chrome一樣,如果放body里則會(huì)出現(xiàn)FOUC無樣式內(nèi)容閃爍。
綜上:如果你想讓頁(yè)面不閃爍放在head里,如果你想讓頁(yè)面不白屏放在body里。
(2)JS
如果JS文件很小放在前面head里或后邊body閉合標(biāo)簽之前都可以。如果JS文件很大則應(yīng)該放在后面body的閉合標(biāo)簽之前。
因?yàn)樵诩虞d JavaScript時(shí)會(huì)阻止其他內(nèi)容的下載,要等到JS文件下載解析完之后才會(huì)顯示網(wǎng)頁(yè)內(nèi)容。若JS文件很大放在前面就會(huì)導(dǎo)致加載時(shí)間較長(zhǎng),網(wǎng)頁(yè)會(huì)一直白屏。還有一個(gè)原因是因?yàn)镴S一般會(huì)涉及到一些DOM操作,所以要等全部的dom元素都加載完再加載JS。
2.為何出現(xiàn)白屏問題與FOUC無樣式內(nèi)容閃爍?
不同的瀏覽器對(duì)于CSS和HTML的處理方式不同,有的是等待CSS加載完成之后,對(duì)HTML元素進(jìn)行渲染和展示(白屏問題)。有的是先對(duì)HTML元素進(jìn)行展示,然后等待CSS加載完成之后重新對(duì)樣式進(jìn)行修改(FOUC無樣式內(nèi)容閃爍)
Firefox、Opere在加載樣式表的時(shí)候是邊加載邊渲染。這樣既有利也有弊:利在使得頁(yè)面可以盡快的開始渲染,而無須等待全部的樣式表都加載下來之后再開始渲染;而弊端在于之前加載并渲染的樣式在后面又重新定義或者修改了布局樣式,那么將會(huì)造成一定程度上的閃爍(FOUC, 即, Flash of Unstyled Content,無樣式內(nèi)容閃爍),閃爍的程度主要看所 影響的元素的范圍和數(shù)目。
而IE、Chrome、Safari則是在全部的樣式表完全加載下來之后才開始渲染頁(yè)面樣式將內(nèi)容呈現(xiàn)在頁(yè)面上,沒下載完之前頁(yè)面是空白的。這樣做也同樣是有利有弊:利在于可以避免 Firefox、Opera中出現(xiàn)的閃爍的問題(雖然在IE下閃爍是經(jīng)常的事情),可以確保樣式會(huì)統(tǒng)一解析并渲染頁(yè)面;而弊端在于頁(yè)面全部樣式表的加載延遲了頁(yè)面渲染的時(shí)間,如果樣式表加載的時(shí)間較長(zhǎng),或者加載錯(cuò)誤,將會(huì)導(dǎo)致頁(yè)面一直處于空白狀態(tài)或者無樣式狀態(tài)。
導(dǎo)致白屏的原因:
樣式文件放在底部,對(duì)于IE瀏覽器,在某些場(chǎng)景下(新窗口打開,刷新等)頁(yè)面會(huì)出現(xiàn)白屏
使用 @import 標(biāo)簽, 即使 CSS 放入 link, 并且放在頭部,也可能出現(xiàn)白屏
把 JavaScript 放入頁(yè)面頂部也會(huì)導(dǎo)致白屏現(xiàn)象,在加載 JavaScript 時(shí),會(huì)禁用并發(fā),并且阻止其他內(nèi)容的下載
導(dǎo)致FOUC的原因 :
- 把樣式放在底部,對(duì)于IE瀏覽器,在某些場(chǎng)景下(點(diǎn)擊鏈接,輸入U(xiǎn)RL,使用書簽進(jìn)入等),會(huì)出現(xiàn) FOUC 現(xiàn)象(逐步加載無樣式的內(nèi)容,等CSS加載后頁(yè)面突然展現(xiàn)樣式)。對(duì)于 Firefox 會(huì)一直表現(xiàn)出 FOUC 。
總結(jié):白屏問題與FOUC無樣式內(nèi)容閃爍只能二選一,不可避免。
3、async和defer的作用是什么?有什么區(qū)別
1.<script src="script.js"></script>
沒有 defer 或 async,瀏覽器會(huì)立即加載并執(zhí)行指定的腳本,“立即”指的是在渲染該 script 標(biāo)簽之下的文檔元素之前,也就是會(huì)打斷后面HTML的解析,只有等該腳本執(zhí)行完成后,瀏覽器才繼續(xù)解析后面的HTML文檔元素。
2.<script defer src="myscript.js"></script>
有 defer,加載后續(xù)文檔元素的過程將和 script.js 的加載并行進(jìn)行(異步),但是 script.js 的執(zhí)行要在所有元素解析完成之后,DOMContentLoaded 事件觸發(fā)之前完成。
3.<script async src="script.js"></script>
有 async,加載和渲染后續(xù)文檔元素的過程將和 script.js 的加載與執(zhí)行并行進(jìn)行(異步)。
然后從實(shí)用角度來說呢,首先把所有腳本都丟到 </body> 之前是最佳實(shí)踐,因?yàn)閷?duì)于舊瀏覽器來說這是唯一的優(yōu)化選擇,此法可保證非腳本的其他一切元素能夠以最快的速度得到加載和解析。
下圖可以非常好的表示這個(gè)過程的差別,其中:藍(lán)色線代表網(wǎng)絡(luò)讀取,紅色線代表執(zhí)行時(shí)間,這倆都是針對(duì)腳本的;綠色線代表 HTML 解析。

上圖告訴我們以下幾個(gè)要點(diǎn):
defer 和 async 在網(wǎng)絡(luò)讀?。ㄏ螺d)這塊兒是一樣的,都是異步的(相較于 HTML 解析)
它倆的差別在于腳本下載完之后何時(shí)執(zhí)行,顯然 defer 是最接近我們對(duì)于應(yīng)用腳本加載和執(zhí)行的要求的
關(guān)于 defer,此圖未盡之處在于它是按照加載順序執(zhí)行腳本的。defer表示腳本可以延遲到文檔完全被解析和顯示之后在執(zhí)行。defer和async都是只對(duì)外部js腳本有效,對(duì)嵌入腳本無效。
async 則是一個(gè)亂序執(zhí)行的主,反正對(duì)它來說腳本的加載和執(zhí)行是緊緊挨著的,所以不管你聲明的順序如何,只要它加載完了就會(huì)立刻執(zhí)行。async表示應(yīng)該立即下載腳本,但不妨礙頁(yè)面中的其他操作,比如下載其他資源或等待加載其他腳本。
仔細(xì)想想,async 對(duì)于應(yīng)用腳本的用處不大,因?yàn)樗耆豢紤]依賴(哪怕是最低級(jí)的順序執(zhí)行),不過它對(duì)于那些可以不依賴任何腳本或不被任何腳本依賴的腳本來說卻是非常合適的,最典型的例子:Google Analytics.
4.簡(jiǎn)述網(wǎng)頁(yè)的渲染機(jī)制
(1) 解析 HTML 標(biāo)簽, 構(gòu)建 DOM 樹
(2) 解析 CSS 標(biāo)簽, 構(gòu)建 CSSOM 樹
(3) 把 DOM 和 CSSOM 組合成 渲染樹 (render tree)
(4) 在渲染樹的基礎(chǔ)上進(jìn)行布局, 計(jì)算每個(gè)節(jié)點(diǎn)的幾何結(jié)構(gòu)
(5)把每個(gè)節(jié)點(diǎn)繪制到屏幕上 (painting)