轉:瀏覽器渲染的原理

瀏覽器渲染過程:javascript

  1. 用戶輸入網址(假設是個 HTML 頁面,而且是第一次訪問),瀏覽器向服務器發出請求,服務器返回 HTML 文件;
  2. 瀏覽器開始載入 HTML 代碼,發現 <head> 標籤內有一個 <link> 標籤引用外部 CSS 文件;
  3. 瀏覽器又發出 CSS 文件的請求,服務器返回這個 CSS 文件;
  4. 瀏覽器繼續載入 HTML 中 <body> 部分的代碼,而且 CSS 文件已經拿到手了,能夠開始渲染頁面了;
  5. 瀏覽器在代碼中發現一個<img> 標籤引用了一張圖片,向服務器發出請求。此時瀏覽器不會等到圖片下載完,而是繼續渲染後面的代碼;
  6. 服務器返回圖片文件,因爲圖片佔用了必定面積,影響了後面段落的排布,所以瀏覽器須要回過頭來從新渲染這部分代碼;
  7. 瀏覽器發現了一個包含一行 JavaScript 代碼的 <script> 標籤,趕快運行它;
  8. JavaScript 腳本執行了這條語句,它命令瀏覽器隱藏掉代碼中的某個 <div>(style.display=」none」)。因爲忽然少了一個元素,瀏覽器不得不從新渲染這部分代碼;
  9. 終於等到了 </html> 的到來,瀏覽器淚流滿面……
  10. 等等,還沒完,用戶點了一下界面中的「換膚」按鈕,JavaScript 讓瀏覽器換了一下 <link> 標籤的 CSS 路徑;
  11. 瀏覽器召集了在座的各位<div><span><ul><li>們,「大夥兒收拾收拾行李,咱得從新來過……」,瀏覽器向服務器請求了新的CSS文件,從新渲染頁面。

簡單來講,瀏覽器渲染一共有五步,
1.解析HTML標籤,構建DOM樹。css

  • 在這個階段,引擎開始解析html,解析出來的結果會成爲一棵dom樹
    dom的目的至少有2個:
    - 做爲下個階段渲染樹狀圖的輸入
    - 成爲網頁和腳本的交互界面。(最經常使用的就是getElementById等等)
    當解析器到達script標籤的時候,發生下面四件事情
    1.html解析器中止解析,
    2.若是是外部腳本,就從外部網絡獲取腳本代碼
    3.將控制權交給js引擎,執行js代碼
    4.恢復html解析器的控制權
    ===由此能夠獲得第一個結論1====
    因爲<script>標籤是阻塞解析的,將腳本放在網頁尾部會加速代碼渲染。
    defer和async屬性也能有助於加載外部腳本。
    defer使得腳本會在dom完整構建以後執行;
    async標籤使得腳本只有在徹底available才執行,而且是以非阻塞的方式進行的。

2.解析CSS標籤,構建CSSOM樹。
咱們已經看到html解析器碰到腳本後會作的事情,接下來咱們看下html解析器碰到樣式表會發生的狀況
js會阻塞解析,由於它會修改文檔(document)。css不會修改文檔的結構,若是這樣的話,彷佛看起來css樣式不會阻塞瀏覽器html解析。可是事實上 css樣式表是阻塞的。阻塞是指當cssom樹創建好以後纔會進行下一步的解析渲染。html

  • 經過如下手段能夠減輕cssom帶來的影響
    將script腳本放在頁面底部
    儘量快的加載css樣式表
    將樣式表按照media type和media query區分,這樣有助於咱們將css資源標記成非阻塞渲染的資源。
    非阻塞的資源仍是會被瀏覽器下載,只是優先級較低。
全部的樣式表都會被解析成cssom對象模型(就和dom樹同樣展示結構),每個頁面element都會被許多css規則匹配
- 匹配順序:origin => weight => specificity
- css origin
-做者自定義
-瀏覽器使用者定義
-userAgent 定義
- css weight
-normal weight
-!important weight
- css specificity (咱們最應該關注的點)
注意:!important 已經被css的做者引入了瀏覽器,用來覆蓋頁面樣式。
原本這個方式並非給開發者使用的,在樣式表中使用!important 會讓咱們忽略specificity 真正工做的原理。
- specificity 是css使人困惑的主要來源。specificity規則由(a,b,c,d)的規則決定。
-a : 值爲1(當樣式放在style屬性中的時候),0爲其它狀況 -b : id在樣式規則中出現的次數 eg:#slide #hello p的值就是2 -c : class,僞類,和屬性在樣式中出現的次數 eg:input[type=email]值就是2 -d : 標籤個數(tag names)和僞類元素出現的次數 例如: Example: HTML <div id=」sidebar」> <div id=」widget」 class=」class-div」> <span class=」span-class」 style=」color: red」>Hello, world!</span> </div> </div> CSS .span-class { /* Specificity (0, 0, 1, 0) */ color: green; } #sidebar #widget { /* Specificity (0, 2, 0, 0) */ color: orange; } #sidebar { /* Specificity (0, 1, 0, 0) */ color: yellow; } #sidebar .class-div { /* Specificity (0, 1, 1, 0) */ color: blue; } The inline rule, will have a specificity of (1, 0, 0, 0). 接下來的優先級是(0, 2, 0, 0);以此類推 - 使用!important 的樣式覆蓋均可以經過css優先級機制(好比加個樣式等來提升優先級) - 總結: -元素樣式的應用按照以下規則排列 -origin -weight -specificity -order of definition specificity只有在origin和weight一致的狀況下才有效,再次就是定義的順序 最後定義的樣式會覆蓋以前的樣式。 - 在解析css的過程當中,cssom樹並非瀏覽器構建的惟一數據結構,css樣式匹配是件重活 爲了儘量快的加載樣式,每一種最精確的匹配規則都會被加入衆多哈希表中的一張 。 有針對id,class類名,標籤名和一些其餘不符合任何規則的哈希表。 當瀏覽器試圖尋找哪一個樣式表加載到元素上的時候,它不必查看全部的規則,而只須要查看哈希表。 這又引導咱們到另外一個重要的知識:咱們從一個元素開始,尋找它的id,class,標籤等,而後在多個字典中查找他們, 咱們老是匹配**最右邊**的(rightmost)的選擇器,這個選擇器稱爲**主選擇器(key selector).** 這個概念開始的時候可能有些難以理解,可是它對咱們如何寫更快的css規則相當重要。 讓咱們看個例子: HTML <div id=」container1」> … thousands of <a> elements here … <a> … </a> … thousands of <a> elements here … </div> <div id=」container2」> <a class=」a-class」>...</a> </div> 咱們假如要選擇container2種的a標籤 This selector: #container2 a {...} 這個選擇器將會嚴重影響加載性能,若是從左到右讀取,那就是先找到#container2而後再找a標籤 ,然而瀏覽器將會從右往左讀取,它會先讀取全部的a標籤,而後再沿着dom往上走,直到找到#container. 這意味着這個規則會尋找全部的a,可是實際上不少a在#container1之中。 咱們能夠寫一個更有效率的樣式表:#container2 .a-class {...}。 這個特性能夠在javascript或者jquery中獲得很好的發揮: eg:將$(‘#container .class-name’)改成$(‘.class-name’, ‘#container’)能夠很好的性能。 -前者會先去尋找.class-name而後再沿着dom樹尋找#container元素 -後者會先找到#container,而後再沿着子樹尋找.class-name的元素

3.把DOM和CSSOM組合成渲染樹(render tree)。render樹java


4.在渲染樹的基礎上進行佈局,計算每一個節點的幾何結構。
佈局(layout):定位座標和大小,是否換行,各類position, overflow, z-index屬性 ……
5.把每一個節點繪製到屏幕上(painting),正式開畫!jquery

相關文章
相關標籤/搜索