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