碎碎念
正式開始學習Javascript慎陵,從T16.x開始慢慢更新Javascript中的學習筆記與感悟
本篇文章包含以下幾個內(nèi)容:
- 白屏與FOUC
- 瀏覽器渲染機制
- reflow&repaint
- 同步&異步疲扎、async&defer
前置知識
在探討白屏問題和FOUC無樣式內(nèi)容閃爍問題出現(xiàn)的原因之前,我們要知道的一個原則是瀏覽器在對于圖片和CSS, 在加載時會并發(fā)加載(如一個域名下同時加載兩個文件). 但在加載 JavaScript 時,會禁用并發(fā),并且阻止其他內(nèi)容的下載爵赵。
白屏與FOUC并不是bug等恐,它僅僅是不同瀏覽器渲染機制的差異洲劣,及不同的代碼順序寫法备蚓,下面來解析下兩種現(xiàn)象的成因。
白屏
現(xiàn)象:瀏覽器頁面在加載時囱稽,出現(xiàn)段時間的白屏星著、無內(nèi)容現(xiàn)象;
原因:在chrome瀏覽器的加載和渲染機制中粗悯,需要等待所有CSS都被加載虚循、解析完成后,再進行頁面渲染样傍。如果CSS樣式放在底部横缔,瀏覽器在渲染完dom后,發(fā)現(xiàn)CSS還沒有加載衫哥、解析茎刚,因此只好再去加載CSS。這個加載等待的時間就出現(xiàn)了長時間的白屏撤逢。
FOUC
現(xiàn)象:無樣式內(nèi)容閃爍膛锭,表現(xiàn)為先展示了沒有樣式(瀏覽器默認樣式)的網(wǎng)頁內(nèi)容,再突然展示加載了樣式的網(wǎng)頁內(nèi)容蚊荣,中間的用戶體驗形象的稱之為『閃爍』初狰。
當我們輸入網(wǎng)址后,瀏覽器會向服務端發(fā)起請求互例,然后服務端將頁面發(fā)送給瀏覽器奢入,瀏覽器邊下載頁面邊進行解析,過程如下:
- 邊下載HTML文檔邊構建DOM樹
- 瀏覽器會先以瀏覽器默認樣式來解析CSSOM Tree
- DOM Tree + CSSOM Tree構建出了渲染樹媳叨,然后頁面內(nèi)容渲染出來
- 當解析到內(nèi)聯(lián)腥光、內(nèi)部樣式時,馬上刷新CSSOM Tree糊秆,引起Render Tree的變化
- 當解析到外部樣式時武福,會先加載,然后解析和刷新CSSOM Tree
上述五個步驟由于樣式文件在下載過程中的延時受網(wǎng)絡環(huán)境與電腦性能的影響痘番,導致我們看到兩種截然不同的樣式的閃爍變化
第二種出現(xiàn)原因是捉片,CSS樣式表被放置在頁面底部,瀏覽器會先加載沒有樣式的HTML頁面夫偶,等CSS加載完界睁,再渲染一次觉增,這就是FOUC兵拢。
CSS、JS文檔推薦的引入順序
- CSS放在
<head>
中逾礁,保證優(yōu)先加載樣式
原因:在body渲染前说铃,先構建一個相對完整的CSSOM Tree
- JS放在最接近
body
閉合標簽處访惜,防止JS的加載阻塞后面標簽與樣式的加載
瀏覽器渲染機制
webkit為內(nèi)核的瀏覽器工作流程:
下面我們具體看下當瀏覽器渲染一個頁面時,發(fā)生了什么腻扇?
- 瀏覽器從服務器獲取到HTML文檔债热,并構建了DOM(文檔對象模型)
- 樣式被載入及解析,構建為CSSOM(層疊樣式表模型)
- 在完成了DOM和CSSOM的基礎上幼苛,渲染樹將會被創(chuàng)建窒篱,代表了一系列將被渲染的對象(在webkit內(nèi)核的瀏覽器中被稱之為renderer或者render object,在Gecko中舶沿,其被稱之為frame)墙杯。渲染樹映射了除了不可見的元素,例如
<head>
和display:none
之外的所有DOM結構括荡,每一個文本字符串都劃分在不同的渲染對象中高镐,每一個渲染對象都包含了它相應的DOM對象以及計算后的樣式。換句話說畸冲,渲染樹是DOM的直觀表示嫉髓。
- 渲染樹的每一個元素包含的內(nèi)容都是計算過的,我們稱之為『布局』邑闲。瀏覽器使用一種流式處理的方法算行,只需要一次操作就可以對所有的元素進行布局(表格則需要多次)。
- 最后苫耸,布局完成纱意,渲染樹將轉化成屏幕上的實際內(nèi)容,我們稱之為『painting』鲸阔。
兩個重要的概念
Repaint/重繪
當頁面元素樣式的修改不影響其在文檔流中的位置(幾何位置偷霉,元素坐標),僅影響元素的外觀時(background-color褐筛,visibility)类少,瀏覽器只會將新樣式賦予元素,并進行重繪渔扎。
觸發(fā)條件:
不涉及DOM元素或排版上的變化硫狞,如元素的color
,text-align
晃痴,另外偽類引起的顏色變化也不會造成reflow残吩,僅僅觸發(fā)repaint。
Reflow/重排
當樣式改變影響到文檔內(nèi)容或結構時倘核,瀏覽器就觸發(fā)Reflow操作(可以理解為重新布局)泣侮,重新計算頁面元素位置或幾何結構。
觸發(fā)條件:
- DOM操作紧唱,即對元素的增刪改活尊,調(diào)整順序等
- 內(nèi)容變化隶校,包括文本的改變
- CSS屬性的更改,或者是重新計算
- 增刪樣式表內(nèi)容
- 瀏覽器窗口變化
- 偽類樣式(:hover等)造成的元素表現(xiàn)得改動
對比
從性能角度考慮蛹锰,Reflow的成本要比Repaint高很多深胳。Dom Tree里面每個節(jié)點都有Reflow方法,一個節(jié)點的reflow會導致子節(jié)點铜犬,父節(jié)點或者兄弟節(jié)點reflow舞终,進而導致性能較低的電腦,或是移動端設備體驗很差癣猾。
因此权埠,在頁面制作過程中,應該盡可能的考慮瀏覽器性能問題煎谍,減少Reflow能夠減少瀏覽器引擎的重新渲染攘蔽,提高頁面加載效率。
哪些樣式是高消耗的呐粘?
具體哪些屬性是高消耗的呢满俗?就是繪制前需要瀏覽器進行大量運算的樣式屬性。
- box-shadows
- border-radius
- transparency(圖像透明度)
- transforms
- CSS filters(性能殺手)
async和defer
前置知識:同步與異步
日常理解中的同步與異步的語義與計算機中的同步作岖、異步是大相徑庭的唆垃。前者在日常對話中往往會說“這幾件工作我們同步進行”,異步就是將事情分開進行痘儡。
在計算機語言中辕万,同步可以理解為,將事情一件一件沉删,按順序往下進行渐尿,而異步理解為幾件事情同時進行,語義與現(xiàn)實生活中的理解正好相反矾瑰。
defer與async屬性
<script src="script.js"></script>
如上砖茸,在無defer與async情況下,瀏覽器會立即加載并執(zhí)行該腳本殴穴。立即指的是凉夯,在渲染該文件 后面的其它元素之前,也就是會阻塞后面HTML元素的解析采幌。只有等腳本解析與執(zhí)行完后劲够,瀏覽器才會繼續(xù)解析其它的元素。
defer與async是腳本異步加載的兩種方式休傍,只對外部腳本有效征绎。我們來具體看看
<script src="script.js" defer></script>
加入了defer,實現(xiàn)了腳本文件與HTML文檔的異步加載尊残,也就是腳本文件的加載與加載后續(xù)文檔元素是并行的炒瘸。這里要注意:腳本文件的執(zhí)行會相應的defer到所有元素解析完成后。
《Javascript高級程序設計》中2.1.2提到寝衫,應用了defer屬性的腳本會先于DOMContentLoaded時間執(zhí)行顷扩。但是,在實際情況中延遲腳本不一定按照順序執(zhí)行慰毅,也不一定在DOMContentLoaded事件觸發(fā)前執(zhí)行隘截。因此,使用defer最大的弊端是執(zhí)行順序是未知的汹胃,同樣也發(fā)生在async上婶芭。
<script src="script.js" aysnc></script>
有 async,加載和渲染后續(xù)文檔元素的過程將和 script.js 的加載與執(zhí)行并行進行(異步)着饥。
總結:
- defer和async在腳本加載方式上都是異步執(zhí)行的犀农;
- 差別在于腳本加載完成后,執(zhí)行腳本的時機宰掉;
- 從執(zhí)行順序上看呵哨,defer是按照加載順序執(zhí)行腳本的,而async是亂序執(zhí)行的轨奄,因為只有某個腳本文件加載完成他就立即執(zhí)行孟害,因此順序無法保證;
- 所以挪拟,從使用場景上看挨务,defer是最接近我們用途的,因為他完全不考慮各個腳本之間的依賴玉组,但它可以用在不受依賴谎柄,獨立的腳本上;