摘要:渲染阻塞在瀏覽器進(jìn)行加載時(shí),其實(shí)是并行加載所有資源。則就叫稱(chēng)為重繪。在回流的時(shí)候,瀏覽器會(huì)使渲染樹(shù)中受到影響的部分失效,并重新構(gòu)造這部分渲染樹(shù),完成回流后,瀏覽器會(huì)重新繪制受影響的部分到屏幕中,該過(guò)程成為重繪。
前面有講到當(dāng)用戶(hù)在瀏覽器輸入url之后,經(jīng)過(guò)一系列的過(guò)程,會(huì)最終向服務(wù)器請(qǐng)求到文檔數(shù)據(jù),文檔數(shù)據(jù)請(qǐng)求到之后,瀏覽器會(huì)將這些數(shù)據(jù)傳給瀏覽器渲染引擎,渲染引擎開(kāi)始正式工作了。
構(gòu)建dom樹(shù),解析css首先瀏覽器接收到html文檔,就會(huì)把HTML在內(nèi)存中轉(zhuǎn)換成DOM樹(shù),HTML中的每個(gè)tag都是DOM樹(shù)中的1個(gè)節(jié)點(diǎn),根節(jié)點(diǎn)就是我們常用的document對(duì)象。DOM樹(shù)里包含了所有HTML標(biāo)簽,包括display:none隱藏,還有用JS動(dòng)態(tài)添加的元素等。在轉(zhuǎn)換的過(guò)程中如果發(fā)現(xiàn)某個(gè)節(jié)點(diǎn)(node)上引用了CSS或者 image,就會(huì)再次向服務(wù)器請(qǐng)求css或image,然后繼續(xù)執(zhí)行構(gòu)建dom樹(shù)的轉(zhuǎn)換,而不需要等待請(qǐng)求的返回,當(dāng)請(qǐng)求的css文件返回后,就會(huì)開(kāi)始解析css style,瀏覽器把所有樣式(用戶(hù)定義的CSS和用戶(hù)代理)解析成樣式結(jié)構(gòu)體,在解析的過(guò)程中會(huì)去掉瀏覽器不能識(shí)別的樣式,比如IE會(huì)去掉-moz開(kāi)頭的樣式,而FF會(huì)去掉_開(kāi)頭的樣式。
構(gòu)建render Tree及繪制DOM Tree 和樣式結(jié)構(gòu)體組合后構(gòu)建render tree,也就是渲染樹(shù)。渲染樹(shù)和dom樹(shù)有很大的區(qū)別,render tree中每個(gè)NODE都有自己的style,而且 render tree不包含隱藏的節(jié)點(diǎn) (比如display:none的節(jié)點(diǎn),還有head節(jié)點(diǎn)),因?yàn)檫@些節(jié)點(diǎn)不會(huì)用于呈現(xiàn),而且不會(huì)影響呈現(xiàn)的,所以就不會(huì)包含到 render tree中。注意 visibility:hidden隱藏的元素還是會(huì)包含到 render tree中的,因?yàn)関isibility:hidden 會(huì)影響布局(layout),會(huì)占有空間。根據(jù)CSS2的標(biāo)準(zhǔn),render tree中的每個(gè)節(jié)點(diǎn)都稱(chēng)為Box (Box dimensions),理解頁(yè)面元素為一個(gè)具有填充、邊距、邊框和位置的盒子。一旦render tree構(gòu)建完畢后,瀏覽器就可以根據(jù)render tree來(lái)繪制頁(yè)面了。
注意:由于瀏覽器的流布局,對(duì)渲染樹(shù)的計(jì)算通常只需要遍歷一次就可以完成。但 table及其內(nèi)部元素除外,它可能需要多次計(jì)算才能確定好其在渲染樹(shù)中節(jié)點(diǎn)的屬性,通常要花3倍于同等元素的時(shí)間。這也是為什么我們要避免使用 table做布局的一個(gè)原因。
渲染阻塞在瀏覽器進(jìn)行加載時(shí),其實(shí)是并行加載所有資源。對(duì)于css和圖片等資源,瀏覽器加載是異步的,并不會(huì)影響到后續(xù)的加載、html解析和后續(xù)渲染。
css阻塞渲染
由上面過(guò)程可以看到,頁(yè)面布局是在渲染樹(shù)構(gòu)建好之后發(fā)生的,而渲染樹(shù)依賴(lài)css樣式結(jié)構(gòu)體,所以CSS 被視為阻塞渲染的資源(但不阻塞html的解析,不會(huì)阻塞dom樹(shù)的構(gòu)建),這意味著瀏覽器將不會(huì)渲染任何已處理的內(nèi)容,直至 CSSOM 構(gòu)建完畢。
因?yàn)閏ss會(huì)阻塞渲染,所以我們應(yīng)該盡早的盡快地下載到客戶(hù)端,以便縮短首次渲染的時(shí)間。平時(shí)在開(kāi)發(fā)的時(shí)候,應(yīng)注意以下幾點(diǎn):
將CSS放在head,不管內(nèi)聯(lián)還是外聯(lián)都盡早開(kāi)始下載或者構(gòu)建CSSOM(前提是這個(gè)CSS是首屏必須的)
避免使用CSS import,CSS中可以用import將另一個(gè)樣式表引入,不過(guò)這樣會(huì)在構(gòu)建CSSOM時(shí)會(huì)增加一次網(wǎng)絡(luò)來(lái)回時(shí)間。
適度內(nèi)聯(lián)CSS,衡量其他因素,如外聯(lián),看網(wǎng)絡(luò)來(lái)回影響多大,考慮css文件的大小
全面考慮渲染情況,網(wǎng)速差、文件下載失敗等,防止白屏?xí)r間太長(zhǎng)
同時(shí),還有以下優(yōu)化點(diǎn):
一、媒體查詢(xún)
通過(guò)使用媒體查詢(xún),我們可以根據(jù)特定的需求(比如顯示或打?。?,也可以根據(jù)動(dòng)態(tài)情況(比如屏幕方向變化、尺寸調(diào)整事件等)定制外觀,
看上面的代碼,
第一行,這樣的普通聲明,會(huì)阻塞渲染
第二行,這個(gè)聲明,只在打印網(wǎng)頁(yè)時(shí)應(yīng)用,因此網(wǎng)頁(yè)在瀏覽器中加載時(shí),不會(huì)阻塞渲染。
第三行,提供了由瀏覽器執(zhí)行的“媒體查詢(xún)”,只有符合條件時(shí),樣式表會(huì)生效,瀏覽器才會(huì)阻塞渲染,直至樣式表下載并處理完畢。
二、preload
preload是resoure hint規(guī)范中定義的一個(gè)功能,顧名思義預(yù)加載,將rel改為preload后,相當(dāng)于加了一個(gè)標(biāo)志位,瀏覽器解析的時(shí)候會(huì)提前建立連接或加載資源,做到盡早并行下載,然后在onload事件響應(yīng)后將link的rel屬性改為stylesheet即可進(jìn)行解析。
IE chrome firefox三者的差異
IE 只要看到HTML 標(biāo)簽就會(huì)進(jìn)行繪制
chrome 不管css放在前面還是后面,都要等到CSSOM構(gòu)建形成后才會(huì)繪制到頁(yè)面上
firefox 放在head則會(huì)阻塞繪制,放在body末尾會(huì)先繪制前面的標(biāo)簽
三、動(dòng)態(tài)添加link
var style = document.createElement("link"); style.rel = "stylesheet"; style.href = "index.css"; document.head.appendChild(style);
js動(dòng)態(tài)添加DOM元素link,不會(huì)阻塞渲染。
loadCSS.js,CSS preload polyfill第三方庫(kù),原理同上
四、代碼簡(jiǎn)練
js阻塞
js可能會(huì)操作html,css,由于瀏覽器不了解腳本計(jì)劃在頁(yè)面上執(zhí)行什么操作,它會(huì)作最壞的假設(shè)并阻止解析器,也就是之前講過(guò)瀏覽器的GUI線(xiàn)程與js引擎線(xiàn)程是互斥的。所以,js會(huì)阻塞渲染
瀏覽器對(duì)于js腳本文件的加載,則會(huì)導(dǎo)致html解析和渲染停止,直至js腳本加載并執(zhí)行完畢才繼續(xù),但是對(duì)于后續(xù)的非js資源加載并不會(huì)停止,瀏覽器會(huì)對(duì)后續(xù)資源進(jìn)行預(yù)加載。而資源加載是屬于另外多帶帶的線(xiàn)程,所以js加載并不會(huì)影響其他非js資源的加載,是瀏覽器的機(jī)制。
總的來(lái)說(shuō)就是以下幾點(diǎn):
js腳本在文檔中的位置很重要,因?yàn)槠涓鷋tml和css有很強(qiáng)的依賴(lài)關(guān)系
在HTML解析器解析到script標(biāo)簽后,會(huì)停止DOM構(gòu)建
javascript可以操作DOM和CSSOM,但進(jìn)行這些行為時(shí)要確保相應(yīng)DOM和CSSOM已經(jīng)存在,
JavaScript 執(zhí)行將暫停,直至 CSSOM 就緒
當(dāng)CSS后面跟著嵌入的JS的時(shí)候,該CSS就會(huì)出現(xiàn)阻塞后面資源下載的情況,因?yàn)闉g覽器會(huì)維持html中css和js的順序,樣式表必須在嵌入的JS執(zhí)行前先加載、解析完。而嵌入的JS會(huì)阻塞后面的資源加載,所以就會(huì)出現(xiàn)CSS阻塞下載的情況。
使用chrome瀏覽器的performance工具查看瀏覽器的渲染過(guò)程:例如下面這段代碼,看瀏覽器是如何一步步將界面繪制出來(lái)
瀏覽器渲染 fgdgg
fgdgg
fgdgg
fgdgg
fgdgg
通過(guò)瀏覽器的工具上的可以很清楚的看到界面的渲染過(guò)程,也可以很清楚的看到請(qǐng)求加載資源的時(shí)候,不會(huì)對(duì)html解析造成影響,但如果資源加載過(guò)慢,會(huì)導(dǎo)致渲染阻塞,通過(guò)此圖可以很好的理解瀏覽器的渲染機(jī)制
如果我把js放在css之后,如下代碼:
瀏覽器渲染 fgdgg
fgdgg
fgdgg
fgdgg
fgdgg
再次查看瀏覽器的渲染過(guò)程:
圖中可以明顯的看出,首先瀏覽器開(kāi)始解析html,然后再解析的過(guò)程中遇到css,開(kāi)始加載css資源,遇到j(luò)s開(kāi)始加載js資源,當(dāng)css加載完成后,開(kāi)始解析css,js加載完成后,則開(kāi)始解析js,此時(shí)解析html生成dom樹(shù)會(huì)停止,直到j(luò)s解析完成之后,才再次開(kāi)始解析html,重新計(jì)算樣式,布局,生成渲染樹(shù),最終才是界面繪制,所以在開(kāi)發(fā)的時(shí)候不要將js文件寫(xiě)在頭部,這樣會(huì)影響界面的繪制,導(dǎo)致界面出現(xiàn)空白
瀏覽器的重繪(repaints)與回流(reflows)重繪
當(dāng)render tree中的一些元素需要更新屬性,而這些屬性只是影響元素的外觀,風(fēng)格,而不會(huì)影響布局的,比如background-color。則就叫稱(chēng)為重繪。
回流
當(dāng)render tree中的一部分(或全部)因?yàn)樵氐囊?guī)模尺寸,布局,隱藏等改變而需要重新構(gòu)建。這就稱(chēng)為回流(reflow)。
每個(gè)頁(yè)面至少需要一次回流,就是在頁(yè)面第一次加載的時(shí)候。在回流的時(shí)候,瀏覽器會(huì)使渲染樹(shù)中受到影響的部分失效,并重新構(gòu)造這部分渲染樹(shù),完成回流后,瀏覽器會(huì)重新繪制受影響的部分到屏幕中,該過(guò)程成為重繪。
回流必然會(huì)造成重繪,重繪不會(huì)造成回流。
回流何時(shí)發(fā)生:
當(dāng)頁(yè)面布局和幾何屬性改變時(shí)就需要回流。下述情況會(huì)發(fā)生瀏覽器回流:
1、添加或者刪除可見(jiàn)的DOM元素;
2、元素位置改變;
3、元素尺寸改變——邊距、填充、邊框、寬度和高度
4、內(nèi)容改變——比如文本改變或者圖片大小改變而引起的計(jì)算值寬度和高度改變;
5、頁(yè)面渲染初始化;
6、瀏覽器窗口尺寸改變——resize事件發(fā)生時(shí);
回流比重繪的代價(jià)要更高,回流的花銷(xiāo)跟render tree有多少節(jié)點(diǎn)需要重新構(gòu)建有關(guān)系,假設(shè)你直接操作body,比如在body最前面插入1個(gè)元素,會(huì)導(dǎo)致整個(gè)render tree回流,這樣代價(jià)當(dāng)然會(huì)比較高,但如果是指body后面插入1個(gè)元素,則不會(huì)影響前面元素的回流。
如果每句JS操作都去回流重繪的話(huà),瀏覽器可能就會(huì)受不了。所以很多瀏覽器都會(huì)優(yōu)化這些操作,瀏覽器會(huì)維護(hù)1個(gè)隊(duì)列,把所有會(huì)引起回流、重繪的操作放入這個(gè)隊(duì)列,等隊(duì)列中的操作到了一定的數(shù)量或者到了一定的時(shí)間間隔,瀏覽器就會(huì)flush隊(duì)列,進(jìn)行一個(gè)批處理。這樣就會(huì)讓多次的回流、重繪變成一次回流重繪。
雖然有了瀏覽器的優(yōu)化,但有時(shí)候我們寫(xiě)的一些代碼可能會(huì)強(qiáng)制瀏覽器提前flush隊(duì)列,這樣瀏覽器的優(yōu)化可能就起不到作用了。當(dāng)你請(qǐng)求向?yàn)g覽器請(qǐng)求一些 style信息的時(shí)候,就會(huì)讓瀏覽器flush隊(duì)列,比如:
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
width,height
請(qǐng)求了getComputedStyle(), 或者 IE的 currentStyle
當(dāng)請(qǐng)求上面的一些屬性的時(shí)候,瀏覽器為了給你最精確的值,需要flush隊(duì)列,因?yàn)殛?duì)列中可能會(huì)有影響到這些值的操作。即使你獲取元素的布局和樣式信息跟最近發(fā)生或改變的布局信息無(wú)關(guān),瀏覽器都會(huì)強(qiáng)行刷新渲染隊(duì)列。
盡量減少回流和重繪因?yàn)榛亓鞯拈_(kāi)銷(xiāo)很大,所以我們?cè)趯?xiě)代碼的時(shí)候,有很多需要注意的地方:
不要一個(gè)一個(gè)改變?cè)氐臉邮綄傩裕詈弥苯痈淖僣lassName,但className是預(yù)先定義好的樣式,不是動(dòng)態(tài)的,如果你要?jiǎng)討B(tài)改變一些樣式,則使用cssText來(lái)改變,如下:
// 不好的寫(xiě)法 var left = 1; var top = 1; el.style.left = left + "px"; el.style.top = top + "px"; // 比較好的寫(xiě)法 el.className += " className1"; // 比較好的寫(xiě)法 el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
讓要操作的元素進(jìn)行"離線(xiàn)處理",處理完后一起更新,這里所謂的"離線(xiàn)處理"即讓元素不存在于render tree中
a、使用documentFragment或div等元素進(jìn)行緩存操作,這個(gè)主要用于添加元素的時(shí)候,大家應(yīng)該都用過(guò),就是先把所有要添加到元素添加到1個(gè)div(這個(gè)div也是新加的),最后才把這個(gè)div append到body中。
b、先display:none 隱藏元素,然后對(duì)該元素進(jìn)行所有的操作,最后再顯示該元素。因?qū)isplay:none的元素進(jìn)行操作不會(huì)引起回流、重繪。所以只要操作只會(huì)有2次回流。
不要經(jīng)常訪(fǎng)問(wèn)會(huì)引起瀏覽器flush隊(duì)列的屬性,如果你確實(shí)要訪(fǎng)問(wèn),就先讀取到變量中進(jìn)行緩存,以后用的時(shí)候直接讀取變量就可以了,見(jiàn)下面代碼:
// 別這樣寫(xiě) for(循環(huán)) { elel.style.left = el.offsetLeft + 5 + "px"; elel.style.top = el.offsetTop + 5 + "px"; } // 這樣寫(xiě)好點(diǎn) var left = el.offsetLeft,top = el.offsetTop,s = el.style; for(循環(huán)) { left += 10; top += 10; s.left = left + "px"; s.top = top + "px"; }
考慮你的操作會(huì)影響到render
tree中的多少節(jié)點(diǎn)以及影響的方式,影響越多,花費(fèi)肯定就越多。比如現(xiàn)在很多人使用jquery的animate方法移動(dòng)元素來(lái)展示一些動(dòng)畫(huà)效果,想想下面2種移動(dòng)的方法:
// block1是position:absolute 定位的元素,它移動(dòng)會(huì)影響到它父元素下的所有子元素。 // 因?yàn)樵谒苿?dòng)過(guò)程中,所有子元素需要判斷block1的z-index是否在自己的上面, // 如果是在自己的上面,則需要重繪,這里不會(huì)引起回流 $("#block1").animate({left:50}); // block2是相對(duì)定位的元素,這個(gè)影響的元素與block1一樣,但是因?yàn)閎lock2非絕對(duì)定位 // 而且改變的是marginLeft屬性,所以這里每次改變不但會(huì)影響重繪, // 還會(huì)引起父元素及其下元素的回流 $("#block2").animate({marginLeft:50});
參考文章:
https://www.cnblogs.com/kevin...
https://blog.csdn.net/allenli...
https://www.css88.com/archive...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/114284.html
摘要:渲染阻塞在瀏覽器進(jìn)行加載時(shí),其實(shí)是并行加載所有資源。則就叫稱(chēng)為重繪。在回流的時(shí)候,瀏覽器會(huì)使渲染樹(shù)中受到影響的部分失效,并重新構(gòu)造這部分渲染樹(shù),完成回流后,瀏覽器會(huì)重新繪制受影響的部分到屏幕中,該過(guò)程成為重繪。 前面有講到當(dāng)用戶(hù)在瀏覽器輸入url之后,經(jīng)過(guò)一系列的過(guò)程,會(huì)最終向服務(wù)器請(qǐng)求到文檔數(shù)據(jù),文檔數(shù)據(jù)請(qǐng)求到之后,瀏覽器會(huì)將這些數(shù)據(jù)傳給瀏覽器渲染引擎,渲染引擎開(kāi)始正式工作了。 構(gòu)建...
摘要:渲染阻塞在瀏覽器進(jìn)行加載時(shí),其實(shí)是并行加載所有資源。則就叫稱(chēng)為重繪。在回流的時(shí)候,瀏覽器會(huì)使渲染樹(shù)中受到影響的部分失效,并重新構(gòu)造這部分渲染樹(shù),完成回流后,瀏覽器會(huì)重新繪制受影響的部分到屏幕中,該過(guò)程成為重繪。 前面有講到當(dāng)用戶(hù)在瀏覽器輸入url之后,經(jīng)過(guò)一系列的過(guò)程,會(huì)最終向服務(wù)器請(qǐng)求到文檔數(shù)據(jù),文檔數(shù)據(jù)請(qǐng)求到之后,瀏覽器會(huì)將這些數(shù)據(jù)傳給瀏覽器渲染引擎,渲染引擎開(kāi)始正式工作了。 構(gòu)建...
摘要:何時(shí)發(fā)生有大量的用戶(hù)行為以及潛在的改變會(huì)觸發(fā)回流。這樣就會(huì)讓多次的回流重繪變成一次回流重繪。因?yàn)樯系牟僮鞑粫?huì)引發(fā)回流和重繪。參考文章回流與重繪性能讓變慢參考文章瀏覽器的重繪與重排 推薦了解的知識(shí):基本的HTML,基本的JavaScript,以及一些css工作原理方面的知識(shí) 瀏覽器的渲染原理 css的加載和解析不會(huì)阻塞html文檔的解析 css的解析會(huì)阻塞js的執(zhí)行,必須等到CSSOM...
摘要:由一道面試題引發(fā)的思考從用戶(hù)輸入瀏覽器輸入到頁(yè)面最后呈現(xiàn)有哪些過(guò)程一道很常規(guī)的題目,考的是基本網(wǎng)絡(luò)原理,和瀏覽器加載,過(guò)程。所以抽出時(shí)間研究下瀏覽器渲染頁(yè)面的過(guò)程。 由一道面試題引發(fā)的思考: 從用戶(hù)輸入瀏覽器輸入url到頁(yè)面最后呈現(xiàn) 有哪些過(guò)程?一道很常規(guī)的題目,考的是基本網(wǎng)絡(luò)原理,和瀏覽器加載css,js過(guò)程。 答案大致如下: 用戶(hù)輸入U(xiǎn)RL地址 瀏覽器解析URL解析出主機(jī)名 瀏覽...
摘要:引言一直對(duì)瀏覽器的進(jìn)程線(xiàn)程的運(yùn)行一無(wú)所知,經(jīng)過(guò)一次的刷刷刷相關(guān)的博客之后,對(duì)其有了初步的了解,是時(shí)候該總結(jié)一波了。瀏覽器內(nèi)的進(jìn)程知道了進(jìn)程與線(xiàn)程之間的關(guān)系之后,下面是瀏覽器與進(jìn)程的關(guān)系了。 引言 一直對(duì)瀏覽器的進(jìn)程、線(xiàn)程的運(yùn)行一無(wú)所知,經(jīng)過(guò)一次的刷刷刷相關(guān)的博客之后,對(duì)其有了初步的了解,是時(shí)候該總結(jié)一波了。 進(jìn)程、線(xiàn)程之間的關(guān)系 一個(gè)進(jìn)程有一個(gè)或多個(gè)線(xiàn)程,線(xiàn)程之間共同完成進(jìn)程分配下來(lái)的...
閱讀 3186·2021-09-10 10:51
閱讀 3368·2021-08-31 09:38
閱讀 1660·2019-08-30 15:54
閱讀 3147·2019-08-29 17:22
閱讀 3225·2019-08-26 13:53
閱讀 1978·2019-08-26 11:59
閱讀 3294·2019-08-26 11:37
閱讀 3324·2019-08-26 10:47