1. 程式人生 > >瀏覽器渲染原理及web前端分析,從瀏覽器渲染原理談頁面優化

瀏覽器渲染原理及web前端分析,從瀏覽器渲染原理談頁面優化

瀏覽器渲染原理及web前端分析
瀏覽器的主要功能
使用者介面:包括位址列、後退/前進按鈕、書籤目錄等,也就是除了用來顯示你所請求頁面的主視窗之外的其他部分。

瀏覽器引擎:用來查詢及操作渲染引擎的介面。另外還用來操作瀏覽器的資料儲存。

渲染引擎:用來顯示請求的內容,例如,如果請求內容為html,它負責解析html及css,並將解析後的結果顯示出來。

網路:用來完成網路呼叫,例如http請求,它具有平臺無關的介面,可以在不同平臺上工作。

UI後端:用來繪製類似組合選擇框及對話方塊等基本元件,具有不特定於某個平臺的通用介面,底層使用作業系統的使用者介面。

JS直譯器:用來解釋執行JS程式碼。

資料儲存:屬於持久層,瀏覽器需要在硬碟中儲存類似cookie的各種資料

圖:瀏覽器主要元件圖

瀏覽器渲染機制


我們現在要在瀏覽器中顯示以上結構的HTML:

1.客戶端瀏覽器開始從使用者向伺服器發出請求,伺服器響應的將如上的HTML返回給請求的客戶端瀏覽器中。

2.載入從標籤開始,並發現標籤內有個外部樣式檔案要載入

<link href="../css/css.css" rel="Stylesheet" type="text/css" />
1
3.這時客戶端瀏覽器又向伺服器發出一個請求載入css.css檔案,伺服器響應。

4.此時客戶端瀏覽器繼續載入html檔案裡裡的標籤,在css.css檔案載入完後,同時開始渲染頁面。

5.客戶端瀏覽器在裡的標籤中發現一個標籤並且引用了伺服器進而的一張名為test.jpg的圖片.客戶端瀏覽器又向伺服器發出一次請求。而瀏覽器不會因為此時正在載入標籤裡的圖片而停止等待圖片載入完,瀏覽器繼續渲染還未完成的部分。

6.標籤裡的圖片載入完了,則要顯示出來,同時圖片會佔有一定的面積,又會影響到後面的佈局,瀏覽器不得不又回來重新渲染一次這部分。

7.總算把body裡的標籤載入及渲染完後,瀏覽器又發現了一段程式碼:

<script type="text/javascript" src="js/js.js"></script>
1
8.瀏覽器又立刻向伺服器發出請求載入js.js檔案,伺服器響應。

9.瀏覽器又在js.js檔案中發現了一段程式碼是讓

標籤隱藏的程式碼,此時瀏覽器又要重新去渲染被隱藏的
部分。
10.最後到瀏覽器發現了為止。

參考資料: 
深入學習,偏底層(對於架構的理解更透徹) 
http://blog.csdn.net/findsafety/article/details/50424307

WEB前端分析測試點
12個測試要點

優點: 
純粹的前端視角,可以測試任何的網站,不需要考慮後臺

1.減少http請求的數量(把js,css合併),為了儘可能減少tcp連結的建立和釋放連線的資源,減少讀取資源讀取IO的時間

2.用好瀏覽器快取,瀏覽器會把所有的訪問記錄儲存在硬碟中,下次訪問時會判斷時候已儲存,提升訪問速度。

3.利用gzip壓縮機制,只針對文字類資源有效(js,css,html),文字的壓縮比可以達到70%多。gif,png,等圖片資源其實已經被壓縮過了

4.把CSS檔案放在html的開頭,讓瀏覽器一開始把渲染資源下載下來,保證以後的頁面渲染比較流暢。 
css主要用來渲染,當發現有css會優先下載下來,便於渲染。

5.儘可能把js放在html的結尾,當頁面渲染完成後,使用者操作時候能達到動態的效果

6.儘量避免css表示式,判斷瀏覽器的版本,解析度等表示式

7.儘可能減少DNS查詢,dns本身會消耗時間,儘可能使用相對路徑。

DNS作用:把域名解析成IP地址

8.最好使用js壓縮,比gzip更有針對性,js壓縮並不是需要解壓縮,而是壓縮了以後js程式碼同樣可以工作.

eg.var username–> var u; 
把長的字元變成短的字元,同樣適用於css,html

9.儘量避免重定向redirect eg.訪問www.baidu.com/ 
/本身也是一種重定向,我們網頁內部儘可能制定到某一個指定的地址,以減少資源

10.去除重複的指令碼,指令碼重複太多會增大指令碼的大小,消耗頻寬資源 
如:加減法的函式,可以合併成一個操作函式

11.使用ajax請求,區域性某些資料會變化,需要什麼內容只請求那一部分內容 
提升使用者體驗,節省很多網路傳輸的頻寬成本。 
目前,我們用jQuery js的框架寫ajax非常方便.

12.使用CDN,內容分發網路。cdn不是從前端可控的技術,很大的快取伺服器 
減少總伺服器的訪問壓力,提升使用者的訪問速度.
--------------------- 
作者:ElenaYu 
來源:CSDN 
原文:https://blog.csdn.net/yu1014745867/article/details/78518296?utm_source=copy 
版權宣告:本文為博主原創文章,轉載請附上博文連結!

一、關於瀏覽器渲染的容易誤解點總結 
關於瀏覽器渲染機制已經是老生常談,如果你想了解請點這裡,而且網上現有資料中有非常多的優秀資料對此進行闡述。遺憾的是網上的資料良莠不齊,經常在不同的文件中對同一件事的描述出現了極大的差異。懷著嚴謹求學的態度經過大量資料的查閱和請教,將會在後文總結出一個完整的流程。在這裡將會就一些我自己理解存疑的地方寫出來 
1、DOM樹的構建是文件載入完成開始的? 
DOM樹的構建是從接受到文件開始的 一邊會進行將位元組轉化為字元 字元轉化為標記 標記構建dom樹 
這個過程被分為標記化和樹構建 
而這是一個漸進的過程。為達到更好的使用者體驗,呈現引擎會力求儘快將內容顯示在螢幕上。它不必等到整個 HTML 文件解析完畢之後,就會開始構建呈現樹和設定佈局。在不斷接收和處理來自網路的其餘內容的同時,呈現引擎會將部分內容解析並顯示出來。 
參考文件:http://taligarsiel.com/Projects/howbrowserswork1.htm 
2、渲染樹是在DOM樹和CSS樣式樹構建完畢才開始構建的嗎? 
這三個過程在實際進行的時候又不是完全獨立,而是會有交叉。會造成一邊載入,一邊解析,一邊渲染的工作現象。 
參考文件:http://www.jianshu.com/p/2d522fc2a8f8 
3、css的標籤巢狀越多,越容易定位到元素 
css的解析是自右至左逆向解析的,巢狀越多越增加瀏覽器的工作量,而不會越快。 
因為如果正向解析,例如「div div p em」,我們首先就要檢查當前元素到 html 的整條路徑,找到最上層的 div,再往下找,如果遇到不匹配就必須回到最上層那個 div,往下再去匹配選擇器中的第一個 div,回溯若干次才能確定匹配與否,效率很低。 
逆向匹配則不同,如果當前的 DOM 元素是 div,而不是 selector 最後的 em,那隻要一步就能排除。只有在匹配時,才會不斷向上找父節點進行驗證。 
打個比如 p span.showing 
你認為從一個p元素下面找到所有的span元素並判斷是否有class showing快,還是找到所有的span元素判斷是否有class showing並且包括一個p父元素快 
參考文件:http://www.imooc.com/code/4570

二、頁面渲染的完整流程 
當瀏覽器拿到HTTP報文時呈現引擎將開始解析 HTML 文件,並將各標記逐個轉化成“內容樹”上的 DOM 節點。同時也會解析外部 CSS 檔案以及樣式元素中的樣式資料。HTML 中這些帶有視覺指令的樣式資訊將用於建立另一個樹結構:呈現樹。瀏覽器將根據呈現樹進行佈局繪製。 
  以上就是頁面渲染的大致流程。那麼瀏覽器從使用者輸入網址之後到底做了什麼呢?以下將會進行一個完整的梳理。鑑於本文是前端向的所以梳理內容會有所偏重。而從輸入到呈現可以分為兩個部分:網路通訊和頁面渲染 
我們首先來看網路通訊部分:

1、使用者輸入url並敲擊回車。 
2、進行DNS解析。如果使用者輸入的是ip地址則直接進入第三條。但去記錄毫無規律且冗長的ip地址顯然不是易事,所以通常都是輸入的域名,此時就會進行dns解析。所謂DNS(Domain Name System)指域名系統。因特網上作為域名和IP地址相互對映的一個分散式資料庫,能夠使使用者更方便的訪問網際網路,而不用去記住能夠被機器直接讀取的IP數串。通過主機名,最終得到該主機名對應的IP地址的過程叫做域名解析(或主機名解析)。這個過程如下所示:

瀏覽器會首先搜尋瀏覽器自身的DNS快取(快取時間比較短,大概只有2分鐘左右,且只能容納1000條快取)。

如果瀏覽器自身快取找不到則會檢視系統的DNS快取,如果找到且沒有過期則停止搜尋解析到此結束.
而如果本機沒有找到DNS快取,則瀏覽器會發起一個DNS的系統呼叫,就會向本地配置的首選DNS伺服器發起域名解析請求(通過的是UDP協議向DNS的53埠發起請求,這個請求是遞迴的請求,也就是運營商的DNS伺服器必須得提供給我們該域名的IP地址),運營商的DNS伺服器首先查詢自身的快取,找到對應的條目,且沒有過期,則解析成功。
如果沒有找到對應的條目,則有運營商的DNS代我們的瀏覽器發起迭代DNS解析請求,它首先是會找根域的DNS的IP地址(這個DNS伺服器都內建13臺根域的DNS的IP地址),找打根域的DNS地址,就會向其發起請求(請問www.xxxx.com這個域名的IP地址是多少啊?)
根域發現這是一個頂級域com域的一個域名,於是就告訴運營商的DNS我不知道這個域名的IP地址,但是我知道com域的IP地址,你去找它去,於是運營商的DNS就得到了com域的IP地址,又向com域的IP地址發起了請求(請問www.xxxx.com這個域名的IP地址是多少?),com域這臺伺服器告訴運營商的DNS我不知道www.xxxx.com這個域名的IP地址,但是我知道xxxx.com這個域的DNS地址,你去找它去,於是運營商的DNS又向linux178.com這個域名的DNS地址(這個一般就是由域名註冊商提供的,像萬網,新網等)發起請求(請問www.xxxx.com這個域名的IP地址是多少?),這個時候xxxx.com域的DNS伺服器一查,誒,果真在我這裡,於是就把找到的結果傳送給運營商的DNS伺服器,這個時候運營商的DNS伺服器就拿到了www.xxxx.com這個域名對應的IP地址,並返回給Windows系統核心,核心又把結果返回給瀏覽器,終於瀏覽器拿到了www.xxxx.com對應的IP地址,這次dns解析圓滿成功。

3、發起TCP的3次握手 
拿到域名對應的IP地址之後,User-Agent(一般是指瀏覽器)會以一個隨機埠(1024< 埠 < 65535)向伺服器的WEB程式(常用的有httpd,nginx等)80埠發起TCP的連線請求。這個連線請求(原始的http請求經過TCP/IP4層模型的層層封包)到達伺服器端後(這中間通過各種路由裝置,區域網內除外),進入到網絡卡,然後是進入到核心的TCP/IP協議棧(用於識別該連線請求,解封包,一層一層的剝開),還有可能要經過Netfilter防火牆(屬於核心的模組)的過濾,最終到達WEB程式,最終建立了TCP/IP的連線。

4、建立TCP連線後發起http請求

5、伺服器端響應http請求,瀏覽器得到html程式碼。以下為響應報文格式: 


以上是網路通訊部分,接下來將會對頁面渲染部分進行敘述。當瀏覽器拿到html後是如何進行頁面渲染的
當瀏覽器拿到HTML文件時首先會進行HTML文件解析,構建DOM樹。
遇到css樣式如link標籤或者style標籤時開始解析css,構建樣式樹。HTML解析構建和CSS的解析是相互獨立的並不會造成衝突,因此我們通常將css樣式放在head中,讓瀏覽器儘早解析css。
當html的解析遇到script標籤會怎樣呢?答案是停止DOM樹的解析開始下載js。因為js是會阻塞html解析的,是阻塞資源。其原因在於js可能會改變html現有結構。例如有的節點是用js動態構建的,在這種情況下就會停止dom樹的構建開始下載解析js。指令碼在文件的何處插入,就在何處執行。當 HTML 解析器遇到一個 script 標記時,它會暫停構建 DOM,將控制權移交給 JavaScript 引擎;等 JavaScript 引擎執行完畢,瀏覽器會從中斷的地方恢復 DOM 構建。而因此就會推遲頁面首繪的時間。可以在首繪不需要js的情況下用async和defer實現非同步載入。這樣js就不會阻塞html的解析了。當HTML解析完成後,瀏覽器會將文件標註為互動狀態,並開始解析那些處於“deferred”模式的指令碼,也就是那些應在文件解析完成後才執行的指令碼。然後,文件狀態將設定為“完成”,一個“載入”事件將隨之觸發。注意,非同步執行是指下載。執行js時仍然會阻塞。
在得到DOM樹和樣式樹後就可以進行渲染樹的構建了。應注意的是渲染樹和 DOM 元素相對應的,但並非一一對應。比如非視覺化的 DOM 元素不會插入呈現樹中,例如“head”元素。如果元素的 display 屬性值為“none”,那麼也不會顯示在呈現樹中(但是 visibility 屬性值為“hidden”的元素仍會顯示) 

渲染樹構建完畢後將會進行佈局。佈局使用流模型的Layout演算法。所謂流模型,即是指Layout的過程只需進行一遍即可完成,後出現在流中的元素不會影響前出現在流中的元素,Layout過程只需從左至右從上至下一遍完成即可。但實際實現中,流模型會有例外。Layout是一個遞迴的過程,每個節點都負責自己及其子節點的Layout。Layout結果是相對父節點的座標和尺寸。其過程可以簡述為:

父節點確定自己的寬度
父節點完成子節點放置,確定其相對座標
節點確定自己的寬度和高度
父節點根據所有的子節點高度計算自己的高度
1
2
3
4
此時renderTree已經構建完畢,不過瀏覽器渲染樹引擎並不直接使用渲染樹進行繪製,為了方便處理定位(裁剪),溢位滾動(頁內滾動),CSS轉換/不透明/動畫/濾鏡,蒙版或反射,Z (Z排序)等,瀏覽器需要生成另外一棵樹 - 層樹。因此繪製過程如下: 
獲取 DOM 並將其分割為多個層(RenderLayer) 
將每個層柵格化,並獨立的繪製進點陣圖中 
將這些點陣圖作為紋理上傳至 GPU 
複合多個層來生成最終的螢幕影象(終極layer)。

三、HTML及CSS樣式的解析 
HTML解析是一個將位元組轉化為字元,字元解析為標記,標記生成節點,節點構建樹的過程。。CSS樣式的解析則由於複雜的樣式層疊而變得複雜。對此不同的渲染引擎在處理上有所差異,後文將會就這點進行詳細講解
1、HTML的解析分為標記化和樹構建兩個階段 
標記化演算法: 
是詞法分析過程,將輸入內容解析成多個標記。HTML標記包括起始標記、結束標記、屬性名稱和屬性值。標記生成器識別標記,傳遞給樹構造器,然後接受下一個字元以識別下一個標記;如此反覆直到輸入的結束。 
該演算法的輸出結果是 HTML 標記。該演算法使用狀態機來表示。每一個狀態接收來自輸入資訊流的一個或多個字元,並根據這些字元更新下一個狀態。當前的標記化狀態和樹結構狀態會影響進入下一狀態的決定。這意味著,即使接收的字元相同,對於下一個正確的狀態也會產生不同的結果,具體取決於當前的狀態。 
樹構建演算法 
在樹構建階段,以 Document 為根節點的 DOM 樹也會不斷進行修改,向其中新增各種元素。 
標記生成器傳送的每個節點都會由樹構建器進行處理。規範中定義了每個標記所對應的 DOM 元素,這些元素會在接收到相應的標記時建立。這些元素不僅會新增到 DOM 樹中,還會新增到開放元素的堆疊中。此堆疊用於糾正巢狀錯誤和處理未關閉的標記。其演算法也可以用狀態機來描述。這些狀態稱為“插入模式”。

以下將會舉一個例子來分析這兩個階段:

<html>
  <body>
    Hello world
  </body>
</html>