摘要:由一道面試題引發的思考從用戶輸入瀏覽器輸入到頁面最后呈現有哪些過程一道很常規的題目,考的是基本網絡原理,和瀏覽器加載,過程。所以抽出時間研究下瀏覽器渲染頁面的過程。
由一道面試題引發的思考:
從用戶輸入瀏覽器輸入url到頁面最后呈現 有哪些過程?
一道很常規的題目,考的是基本網絡原理,和瀏覽器加載css,js過程。
答案大致如下:
用戶輸入URL地址
瀏覽器解析URL解析出主機名
瀏覽器將主機名轉換成服務器ip地址(瀏覽器先查找本地DNS緩存列表 沒有的話 再向瀏覽器默認的DNS服務器發送查詢請求 同時緩存)
瀏覽器將端口號從URL中解析出來
瀏覽器建立一條與目標Web服務器的TCP連接(三次握手)
瀏覽器向服務器發送一條HTTP請求報文
服務器向瀏覽器返回一條HTTP響應報文
關閉連接 瀏覽器解析文檔
如果文檔中有資源 重復6 7 8 動作 直至資源全部加載完畢
以上答案基本簡述了一個網頁基本的響應過程背后的原理。
但這也只是一部分,瀏覽器獲取數據的部分,至于瀏覽器拿到數據之后,怎么渲染頁面的,一直沒太關注。
所以抽出時間研究下瀏覽器渲染頁面的過程。
通過研究,了解一些基本常識的原理:
為什么要將js放到頁腳部分
引入樣式的幾種方式的權重
css屬性書寫順序建議
何種類型的DOM操作是耗費性能的
瀏覽器渲染主要流程不同的瀏覽器內核不同,所以渲染過程不太一樣。
WebKit 主流程
Mozilla 的 Gecko 呈現引擎主流程
由上面兩張圖可以看出,雖然主流瀏覽器渲染過程叫法有區別,但是主要流程還是相同的。
Gecko 將視覺格式化元素組成的樹稱為“框架樹”。每個元素都是一個框架。WebKit 使用的術語是“呈現樹”,它由“呈現對象”組成。對于元素的放置,WebKit 使用的術語是“布局”,而 Gecko 稱之為“重排”。對于連接 DOM 節點和可視化信息從而創建呈現樹的過程,WebKit 使用的術語是“附加”。
所以可以分析出基本過程:
HTML解析出DOM Tree
CSS解析出Style Rules
將二者關聯生成Render Tree
Layout 根據Render Tree計算每個節點的信息
Painting 根據計算好的信息繪制整個頁面
HTML解析HTML Parser的任務是將HTML標記解析成DOM Tree
這個解析可以參考React解析DOM的過程,
但是這里面有很多別的規則和操作,比如容錯機制,識別和
等等。
感興趣的可以參考 《How Browser Work》,中文翻譯
舉個例子:一段HTML
Web page parsing Web page parsing
This is an example Web page.
經過解析之后的DOM Tree差不多就是
將文本的HTML文檔,提煉出關鍵信息,嵌套層級的樹形結構,便于計算拓展。這就是HTML Parser的作用。
CSS解析CSS Parser將CSS解析成Style Rules,Style Rules也叫CSSOM(CSS Object Model)。
StyleRules也是一個樹形結構,根據CSS文件整理出來的類似DOM Tree的樹形結構:
于HTML Parser相似,CSS Parser作用就是將很多個CSS文件中的樣式合并解析出具有樹形結構Style Rules。
腳本處理瀏覽器解析文檔,當遇到標簽的時候,會立即解析腳本,停止解析文檔(因為JS可能會改動DOM和CSS,所以繼續解析會造成浪費)。
如果腳本是外部的,會等待腳本下載完畢,再繼續解析文檔?,F在可以在script標簽上增加屬性 defer或者async。
腳本解析會將腳本中改變DOM和CSS的地方分別解析出來,追加到DOM Tree和Style Rules上。
Render Tree的構建其實就是DOM Tree和CSSOM Attach的過程。
呈現器是和 DOM 元素相對應的,但并非一一對應。Render Tree實際上就是一個計算好樣式,與HTML對應的(包括哪些顯示,那些不顯示)的Tree。
樣式計算在 WebKit 中,解析樣式和創建呈現器的過程稱為“附加”。每個 DOM 節點都有一個“attach”方法。附加是同步進行的,將節點插入 DOM 樹需要調用新的節點“attach”方法。
樣式計算是個很復雜的問題。DOM中的一個元素可以對應樣式表中的多個元素。樣式表包括了所有樣式:瀏覽器默認樣式表,自定義樣式表,inline樣式元素,HTML可視化屬性如:width=100。后者將轉化以匹配CSS樣式。
WebKit 節點會引用樣式對象 (RenderStyle)。這些對象在某些情況下可以由不同節點共享。這些節點是同級關系,并且:
這些元素必須處于相同的鼠標狀態(例如,不允許其中一個是“:hover”狀態,而另一個不是)
任何元素都沒有 ID
標記名稱應匹配
類屬性應匹配
映射屬性的集合必須是完全相同的
鏈接狀態必須匹配
焦點狀態必須匹配
任何元素都不應受屬性選擇器的影響,這里所說的“影響”是指在選擇器中的任何位置有任何使用了屬性選擇器的選擇器匹配
元素中不能有任何 inline 樣式屬性
不能使用任何同級選擇器。WebCore 在遇到任何同級選擇器時,只會引發一個全局開關,并停用整個文檔的樣式共享(如果存在)。這包括 + 選擇器以及 :first-child 和 :last-child 等選擇器。
為了簡化樣式計算,Firefox 還采用了另外兩種樹:規則樹和樣式上下文樹。WebKit 也有樣式對象,但它們不是保存在類似樣式上下文樹這樣的樹結構中,只是由 DOM 節點指向此類對象的相關樣式。
樣式上下文包含端值。要計算出這些值,應按照正確順序應用所有的匹配規則,并將其從邏輯值轉化為具體的值。
例如,如果邏輯值是屏幕大小的百分比,則需要換算成絕對的單位。規則樹的點子真的很巧妙,它使得節點之間可以共享這些值,以避免重復計算,還可以節約空間。
所有匹配的規則都存儲在樹中。路徑中的底層節點擁有較高的優先級。規則樹包含了所有已知規則匹配的路徑。規則的存儲是延遲進行的。規則樹不會在開始的時候就為所有的節點進行計算,而是只有當某個節點樣式需要進行計算時,才會向規則樹添加計算的路徑。
舉個例子 我們有段HTML代碼:
this is a big error this is also a very big error error
another error
對應CSS規則如下:
1. .div {margin:5px;color:black} 2. .err {color:red} 3. .big {margin-top:3px} 4. div span {margin-bottom:4px} 5. #div1 {color:blue} 6. #div2 {color:green}
則CSS形成的規則樹如下圖所示(節點的標記方式為“節點名 : 指向的規則序號”)
假設我們解析 HTML 時遇到了第二個
現在我們需要填充樣式結構。首先要填充的是 margin 結構。由于最后的規則節點 (F) 并沒有添加到 margin 結構,我們需要上溯規則樹,直至找到在先前節點插入中計算過的緩存結構,然后使用該結構。我們會在指定 margin 規則的最上層節點(即 B 節點)上找到該結構。
我們已經有了 color 結構的定義,因此不能使用緩存的結構。由于 color 有一個屬性,我們無需上溯規則樹以填充其他屬性。我們將計算端值(將字符串轉化為 RGB 等)并在此節點上緩存經過計算的結構。
第二個 元素處理起來更加簡單。我們將匹配規則,最終發現它和之前的 span 一樣指向規則 G。由于我們找到了指向同一節點的同級,就可以共享整個樣式上下文了,只需指向之前 span 的上下文即可。
對于包含了繼承自父代的規則的結構,緩存是在上下文樹中進行的(事實上 color 屬性是繼承的,但是 Firefox 將其視為 reset 屬性,并緩存到規則樹上)
所以生成的上下文樹如下:
樣式對象具有與每個可視化屬性一一對應的屬性(均為 CSS 屬性但更為通用)。如果某個屬性未由任何匹配規則所定義,那么部分屬性就可由父代元素樣式對象繼承。其他屬性具有默認值。
如果定義不止一個,就會出現問題,需要通過層疊順序來解決。
一些例子:
* {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */ li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */ li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */ ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */ ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */ h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */ ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */ li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */ #x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */ style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
利用上面的方法,基本可以快速確定不同選擇器的優先級。
布局Layout創建渲染樹后,下一步就是布局(Layout),或者叫回流(reflow,relayout),這個過程就是通過渲染樹中渲染對象的信息,計算出每一個渲染對象的位置和尺寸,將其安置在瀏覽器窗口的正確位置,而有些時候我們會在文檔布局完成后對DOM進行修改,這時候可能需要重新進行布局,也可稱其為回流,本質上還是一個布局的過程,每一個渲染對象都有一個布局或者回流方法,實現其布局或回流。
對渲染樹的布局可以分為全局和局部的,全局即對整個渲染樹進行重新布局,如當我們改變了窗口尺寸或方向或者是修改了根元素的尺寸或者字體大小等;而局部布局可以是對渲染樹的某部分或某一個渲染對象進行重新布局。
大多數web應用對DOM的操作都是比較頻繁,這意味著經常需要對DOM進行布局和回流,而如果僅僅是一些小改變,就觸發整個渲染樹的回流,這顯然是不好的,為了避免這種情況,瀏覽器使用了臟位系統,只有一個渲染對象改變了或者某渲染對象及其子渲染對象臟位值為”dirty”時,說明需要回流。
表示需要布局的臟位值有兩種:
“dirty”–自身改變,需要回流
“children are dirty”–子節點改變,需要回流
布局是一個從上到下,從外到內進行的遞歸過程,從根渲染對象,即對應著HTML文檔根元素,然后下一級渲染對象,如對應著元素,如此層層遞歸,依次計算每一個渲染對象的幾何信息(位置和尺寸)。
每一個渲染對象的布局流程基本如:
1.計算此渲染對象的寬度(width);
2.遍歷此渲染對象的所有子級,依次:
2.1設置子級渲染對象的坐標
2.2判斷是否需要觸發子渲染對象的布局或回流方法,計算子渲染對象的高度(height)
3.設置此渲染對象的高度:根據子渲染對象的累積高,margin和padding的高度設置其高度;
4.設置此渲染對象臟位值為false。
繪制(Painting)在繪制階段,系統會遍歷呈現樹,并調用呈現器的“paint”方法,將呈現器的內容顯示在屏幕上。繪制工作是使用用戶界面基礎組件完成的。
CSS2 規范定義了繪制流程的順序。繪制的順序其實就是元素進入堆棧樣式上下文的順序。這些堆棧會從后往前繪制,因此這樣的順序會影響繪制。塊呈現器的堆棧順序如下:
背景顏色
背景圖片
邊框
子代
輪廓
這里還要說兩個概念,一個是Reflow,另一個是Repaint。這兩個不是一回事。
Repaint ——屏幕的一部分要重畫,比如某個CSS的背景色變了。但是元素的幾何尺寸沒有變。
Reflow 元件的幾何尺寸變了,我們需要重新驗證并計算Render Tree。是Render Tree的一部分或全部發生了變化。這就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的幾何尺寸發生了變化,需要重新布局,也就叫reflow)reflow 會從這個root frame開始遞歸往下,依次計算所有的結點幾何尺寸和位置,在reflow過程中,可能會增加一些frame,比如一個文本字符串必需被包裝起來。
Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每個結點都會有reflow方法,一個結點的reflow很有可能導致子結點,甚至父點以及同級結點的reflow。在一些高性能的電腦上也許還沒什么,但是如果reflow發生在手機上,那么這個過程是非常痛苦和耗電的。 所以,下面這些動作有很大可能會是成本比較高的。
當你增加、刪除、修改DOM結點時,會導致Reflow或Repaint
當你移動DOM的位置,或是搞個動畫的時候。
當你修改CSS樣式的時候。
當你Resize窗口的時候(移動端沒有這個問題),或是滾動的時候。
當你修改網頁的默認字體時。
注:display:none會觸發reflow,而visibility:hidden只會觸發repaint,因為沒有發現位置變化。
基本上來說,reflow有如下的幾個原因:
Initial。網頁初始化的時候。
Incremental。一些Javascript在操作DOM Tree時。
Resize。其些元件的尺寸變了。
StyleChange。如果CSS的屬性發生變化了。
Dirty。幾個Incremental的reflow發生在同一個frame的子樹上。
看幾個例子:
$("body").css("color", "red"); // repaint $("body").css("margin", "2px"); // reflow, repaint var bstyle = document.body.style; // cache bstyle.padding = "20px"; // reflow, repaint bstyle.border = "10px solid red"; // 再一次的 reflow 和 repaint bstyle.color = "blue"; // repaint bstyle.backgroundColor = "#fad"; // repaint bstyle.fontSize = "2em"; // reflow, repaint // new DOM element - reflow, repaint document.body.appendChild(document.createTextNode("dude!"));
當然,我們的瀏覽器是聰明的,它不會像上面那樣,你每改一次樣式,它就reflow或repaint一次。一般來說,瀏覽器會把這樣的操作積攢一批,然后做一次reflow,這又叫異步reflow或增量異步reflow。但是有些情況瀏覽器是不會這么做的,比如:resize窗口,改變了頁面默認的字體,等。對于這些操作,瀏覽器會馬上進行reflow。
但是有些時候,我們的腳本會阻止瀏覽器這么干,比如:如果我們請求下面的一些DOM值:
offsetTop, offsetLeft, offsetWidth, offsetHeight scrollTop/Left/Width/Height clientTop/Left/Width/Height IE中的 getComputedStyle(), 或 currentStyle
因為,如果我們的程序需要這些值,那么瀏覽器需要返回最新的值,而這樣一樣會flush出去一些樣式的改變,從而造成頻繁的reflow/repaint。
Chrome調試工具查看頁面渲染順序頁面的渲染詳細過程可以通過chrome開發者工具中的timeline查看
發起請求;
解析HTML;
解析樣式;
執行JavaScript;
布局;
繪制
頁面渲染優化瀏覽器對上文介紹的關鍵渲染路徑進行了很多優化,針對每一次變化產生盡量少的操作,還有優化判斷重新繪制或布局的方式等等。
在改變文檔根元素的字體顏色等視覺性信息時,會觸發整個文檔的重繪,而改變某元素的字體顏色則只觸發特定元素的重繪;改變元素的位置信息會同時觸發此元素(可能還包括其兄弟元素或子級元素)的布局和重繪。某些重大改變,如更改文檔根元素的字體尺寸,則會觸發整個文檔的重新布局和重繪,據此及上文所述,推薦以下優化和實踐:
HTML文檔結構層次盡量少,最好不深于六層;
腳本盡量后放,放在前即可;
少量首屏樣式內聯放在標簽內;
樣式結構層次盡量簡單;
在腳本中盡量減少DOM操作,盡量緩存訪問DOM的樣式信息,避免過度觸發回流;
減少通過JavaScript代碼修改元素樣式,盡量使用修改class名方式操作樣式或動畫;
動畫盡量使用在絕對定位或固定定位的元素上;
隱藏在屏幕外,或在頁面滾動時,盡量停止動畫;
盡量緩存DOM查找,查找器盡量簡潔;
涉及多域名的網站,可以開啟域名預解析
總結瀏覽器渲染是個很繁瑣的過程,其中每一步都有對應的算法。
了解渲染過程原理可以有針對的性能優化,而且也可以懂得一些基本的要求和規范的原理。
最后文章中間很多語句都是直接復制的原文,自己的語言概況還是不及原文精彩。
《How Browser Work》
瀏覽器的工作原理:新式網絡瀏覽器幕后揭秘
瀏覽器渲染原理
淺析前端頁面渲染機制
瀏覽器 渲染,繪制流程及性能優化
優化CSS重排重繪與瀏覽器性能
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/112271.html
摘要:不推薦移動端瀏覽器前端優化策略相對于桌面端瀏覽器,移動端瀏覽器上有一些較為明顯的特點設備屏幕較小新特性兼容性較好支持一些較新的和特性需要與應用交互等。 GitHub鏈接:https://github.com/zwwill/blo... 圍繞前端的性能多如牛毛,涉及到方方面面,以我我們將圍繞PC瀏覽器和移動端瀏覽器的優化策略進行羅列注意,是羅列不是展開,遇到不會不懂的點還請站外擴展 開車...
摘要:不推薦移動端瀏覽器前端優化策略相對于桌面端瀏覽器,移動端瀏覽器上有一些較為明顯的特點設備屏幕較小新特性兼容性較好支持一些較新的和特性需要與應用交互等。 GitHub鏈接:https://github.com/zwwill/blo... 圍繞前端的性能多如牛毛,涉及到方方面面,以我我們將圍繞PC瀏覽器和移動端瀏覽器的優化策略進行羅列注意,是羅列不是展開,遇到不會不懂的點還請站外擴展 開車...
摘要:不推薦移動端瀏覽器前端優化策略相對于桌面端瀏覽器,移動端瀏覽器上有一些較為明顯的特點設備屏幕較小新特性兼容性較好支持一些較新的和特性需要與應用交互等。 GitHub鏈接:https://github.com/zwwill/blo... 圍繞前端的性能多如牛毛,涉及到方方面面,以我我們將圍繞PC瀏覽器和移動端瀏覽器的優化策略進行羅列注意,是羅列不是展開,遇到不會不懂的點還請站外擴展 開車...
閱讀 3731·2021-10-14 09:43
閱讀 3315·2021-08-25 09:38
閱讀 614·2019-08-30 15:55
閱讀 1353·2019-08-30 13:05
閱讀 2245·2019-08-29 16:05
閱讀 511·2019-08-29 12:58
閱讀 2799·2019-08-29 12:34
閱讀 3247·2019-08-26 12:15