1、CSS和JS在網頁中的放置順序是怎樣的?
(1)CSS
對于谷歌瀏覽器和Safari放在head
里或body
里都一樣。因為它是在全部的樣式表完全加載下來之后才開始渲染頁面,將內容呈現在頁面上。
對于Firefox,head
標簽中的<link rel="stylesheet">
行為與Chrome/Safari中完全一致,這些link
標簽全部加載完成之前,頁面上不顯示內容。而body
標簽中的<link rel="stylesheet">
則不阻塞任何內容顯示,會出現FOUC無樣式內容閃爍。
對于IE/Edge。未加載完成的<link rel="stylesheet">
標簽只阻塞其后面的HTML內容顯示,而對其前面的HTML內容則不阻塞,所以如果將CSS放在前面head里則和Chrome一樣,如果放body里則會出現FOUC無樣式內容閃爍。
綜上:如果你想讓頁面不閃爍放在head里,如果你想讓頁面不白屏放在body里。
(2)JS
如果JS文件很小放在前面head里或后邊body閉合標簽之前都可以。如果JS文件很大則應該放在后面body的閉合標簽之前。
因為在加載 JavaScript時會阻止其他內容的下載,要等到JS文件下載解析完之后才會顯示網頁內容。若JS文件很大放在前面就會導致加載時間較長,網頁會一直白屏。還有一個原因是因為JS一般會涉及到一些DOM操作,所以要等全部的dom元素都加載完再加載JS。
2.為何出現白屏問題與FOUC無樣式內容閃爍?
不同的瀏覽器對于CSS和HTML的處理方式不同,有的是等待CSS加載完成之后,對HTML元素進行渲染和展示(白屏問題)。有的是先對HTML元素進行展示,然后等待CSS加載完成之后重新對樣式進行修改(FOUC無樣式內容閃爍)
Firefox、Opere在加載樣式表的時候是邊加載邊渲染。這樣既有利也有弊:利在使得頁面可以盡快的開始渲染,而無須等待全部的樣式表都加載下來之后再開始渲染;而弊端在于之前加載并渲染的樣式在后面又重新定義或者修改了布局樣式,那么將會造成一定程度上的閃爍(FOUC, 即, Flash of Unstyled Content,無樣式內容閃爍),閃爍的程度主要看所 影響的元素的范圍和數目。
而IE、Chrome、Safari則是在全部的樣式表完全加載下來之后才開始渲染頁面樣式將內容呈現在頁面上,沒下載完之前頁面是空白的。這樣做也同樣是有利有弊:利在于可以避免 Firefox、Opera中出現的閃爍的問題(雖然在IE下閃爍是經常的事情),可以確保樣式會統一解析并渲染頁面;而弊端在于頁面全部樣式表的加載延遲了頁面渲染的時間,如果樣式表加載的時間較長,或者加載錯誤,將會導致頁面一直處于空白狀態或者無樣式狀態。
導致白屏的原因:
樣式文件放在底部,對于IE瀏覽器,在某些場景下(新窗口打開,刷新等)頁面會出現白屏
使用 @import 標簽, 即使 CSS 放入 link, 并且放在頭部,也可能出現白屏
把 JavaScript 放入頁面頂部也會導致白屏現象,在加載 JavaScript 時,會禁用并發,并且阻止其他內容的下載
導致FOUC的原因 :
- 把樣式放在底部,對于IE瀏覽器,在某些場景下(點擊鏈接,輸入URL,使用書簽進入等),會出現 FOUC 現象(逐步加載無樣式的內容,等CSS加載后頁面突然展現樣式)。對于 Firefox 會一直表現出 FOUC 。
總結:白屏問題與FOUC無樣式內容閃爍只能二選一,不可避免。
3、async和defer的作用是什么?有什么區別
1.<script src="script.js"></script>
沒有 defer 或 async,瀏覽器會立即加載并執行指定的腳本,“立即”指的是在渲染該 script 標簽之下的文檔元素之前,也就是會打斷后面HTML的解析,只有等該腳本執行完成后,瀏覽器才繼續解析后面的HTML文檔元素。
2.<script defer src="myscript.js"></script>
有 defer,加載后續文檔元素的過程將和 script.js 的加載并行進行(異步),但是 script.js 的執行要在所有元素解析完成之后,DOMContentLoaded 事件觸發之前完成。
3.<script async src="script.js"></script>
有 async,加載和渲染后續文檔元素的過程將和 script.js 的加載與執行并行進行(異步)。
然后從實用角度來說呢,首先把所有腳本都丟到 </body> 之前是最佳實踐,因為對于舊瀏覽器來說這是唯一的優化選擇,此法可保證非腳本的其他一切元素能夠以最快的速度得到加載和解析。
下圖可以非常好的表示這個過程的差別,其中:藍色線代表網絡讀取,紅色線代表執行時間,這倆都是針對腳本的;綠色線代表 HTML 解析。
上圖告訴我們以下幾個要點:
defer 和 async 在網絡讀取(下載)這塊兒是一樣的,都是異步的(相較于 HTML 解析)
它倆的差別在于腳本下載完之后何時執行,顯然 defer 是最接近我們對于應用腳本加載和執行的要求的
關于 defer,此圖未盡之處在于它是按照加載順序執行腳本的。defer表示腳本可以延遲到文檔完全被解析和顯示之后在執行。defer和async都是只對外部js腳本有效,對嵌入腳本無效。
async 則是一個亂序執行的主,反正對它來說腳本的加載和執行是緊緊挨著的,所以不管你聲明的順序如何,只要它加載完了就會立刻執行。async表示應該立即下載腳本,但不妨礙頁面中的其他操作,比如下載其他資源或等待加載其他腳本。
仔細想想,async 對于應用腳本的用處不大,因為它完全不考慮依賴(哪怕是最低級的順序執行),不過它對于那些可以不依賴任何腳本或不被任何腳本依賴的腳本來說卻是非常合適的,最典型的例子:Google Analytics.
4.簡述網頁的渲染機制
(1) 解析 HTML 標簽, 構建 DOM 樹
(2) 解析 CSS 標簽, 構建 CSSOM 樹
(3) 把 DOM 和 CSSOM 組合成 渲染樹 (render tree)
(4) 在渲染樹的基礎上進行布局, 計算每個節點的幾何結構
(5)把每個節點繪制到屏幕上 (painting)