前端性能優化:細說瀏覽器渲染的重排與重繪

前端性能優化:細說瀏覽器渲染的重排與重繪

來自專欄猿論11 人贊了文章

前端性能優化因為涉及到計算機網路、數據演算法、圖形圖像處理、瀏覽器渲染等多方面計算機知識,常作為前端工程師樂此不疲的技術討論話題,也正因如此,它也是面試時容易被問及的面試題之一。

緣起

本篇文章緣起一次偶然的面試問答所引申出的思考整理,著筆於瀏覽器渲染的角度,探討前端性能優化的思路和實踐建議,當然,瀏覽器渲染是一個複雜的過程,本文筆者將圍繞重排和重繪兩個關鍵詞開始行文。

目錄結構

文章大致行文思路如下:

  • URL從輸入到頁面展示的過程
  • DOM和JavaScript的關係
  • 為什麼操作DOM會很「慢」
  • 瀏覽器解析HTML的過程
  • 重排
  • 重繪
  • 優化方案

URL從輸入到頁面展示的過程

在探討瀏覽器解析html之前,先了解url從輸入到最後頁面渲染的過程是一個很有必要的步驟,它可以幫助我們把握整體流程,讓我們在了解HTML解析細節之前知道它處於整個請求周期中的哪一階段,這對我們構建完善知識圖譜很有幫助。

首先,我們假設輸入的url的請求為最簡單的Http請求,以GET請求為例,大致分以下幾個步驟:

  1. 用戶在瀏覽器的地址欄輸入訪問的URL地址。瀏覽器會先根據這個URL查看瀏覽器緩存-系統緩存-路由器緩存,若緩存中有,直接跳到第6步操作,若沒有,則按照下面的步驟進行操作。
  2. 瀏覽器根據輸入的URL地址解析出主機名。
  3. 瀏覽器將主機名轉換成伺服器ip地址。瀏覽器先查找本地DNS緩存列表,看緩存裡面是否存在這個ip,如果有則進入第4步,如果緩存中不存在這個ip地址,就再向瀏覽器默認的DNS伺服器發送查詢請求,同時緩存當前這個ip到DNS緩存列表中。更詳細步驟參考DNS查找域名的過程。
  4. 拿到ip地址後,瀏覽器再從URL中解析出埠號。
  5. 拿到ip和埠後,瀏覽器會建立一條與目標Web伺服器的TCP連接,也就是傳說中的三次握手。傳送門:完整的tcp鏈接。
  6. 瀏覽器向伺服器發送一條HTTP請求報文。
  7. 伺服器向瀏覽器返回一條HTTP響應報文。
  8. 關閉連接 瀏覽器解析文檔。
  9. 如果文檔中有資源則重複6、7、8動作,直至資源全部載入完畢。

以上步驟簡述了瀏覽器從輸入url到最後頁面呈現的大致過程,但這並不很具體,比如瀏覽器請求報文類型是什麼,會遇到哪些錯誤場景、瀏覽器又是如何解析響應報文等等都沒具體描述。

實際上在http請求方式不同、有無代理、有無負載均衡等不同場景下訪問伺服器的細節流程也會有一些差別,但這並不影響我們對整個訪問環節的理解,有興趣的同學可網上自行詳細了解,在此不做詳述。

DOM和JavaScript的關係

文檔對象模型(DOM)是一個獨立於語言,用於操作XML和HTML文檔的API,在web端,我們常用來操作HTML,但其實DOM也是可以操作XML文檔的。

我們現在知道,DOM是一個獨立於語言的API,換句話說,DOM是一個與語言無關的API,別的語言也可以實現操作DOM的具體api,但是它在瀏覽器中是用JavaScript來實現的,也因此,DOM是現在JavaScript編碼中很重要的一部分,因為JavaScript很多時候都在操作底層文檔。

為什麼操作DOM會很慢

雖然DOM是由JavaScript實現的,但是在瀏覽器中都是把DOM和JavaScript分開來實現的,比如IE中,JavaScript的實現名為JScript,放在jscript.dll文件中,而DOM則放在另一個叫做mshtml.dll的庫中。在Safari中,DOM和渲染是使用Webkit中的WebCore實現,而JavaScript是由獨立的JavaScriptCore引擎實現,同樣在Chrome中,同樣是使用WebCore來實現渲染,而JavaScript引擎則是他們自己研發的V8引擎。

由於DOM和JavaScript是被分開獨立實現的,因此,每一次在通過js操作DOM的時候,就需要先去連接js和DOM,我們可以這樣理解:把DOM和JavaScript比作兩個島,他們之間通過一個收費的橋連接著,每一次訪問DOM的時候,就需要經過這座橋,並且給「過路費」,訪問的次數越多,路費就會越高,並且訪問到DOM後,操作具體的DOM還需要給「操作費」,由於瀏覽器訪問DOM的操作很多,因此,「路費」和「操作費」自然會增加,這就是為什麼操作DOM會很慢的原因

瀏覽器渲染HTML的步驟

HTML渲染大致分為如下幾步:

  1. HTML被HTML解析器解析成DOM Tree, css則被css解析器解析成CSSOM Tree。
  2. DOM Tree和CSSOM Tree解析完成後,被附加到一起,形成渲染樹(Render Tree)。
  3. 節點信息計算(重排),這個過程被叫做Layout(Webkit)或者Reflow(Mozilla)。即根據渲染樹計算每個節點的幾何信息。
  4. 渲染繪製(重繪),這個過程被叫做(Painting 或者 Repaint)。即根據計算好的信息繪製整個頁面。

以上4步簡述瀏覽器的一次渲染過程,理論上,每一次的dom更改或者css幾何屬性更改,都會引起一次瀏覽器的重排/重繪過程,而如果是css的非幾何屬性更改,則只會引起重繪過程。所以說重排一定會引起重繪,而重繪不一定會引起重排。

重排(Relayout/Reflow)

在弄明白什麼是重排之前,我們要知道:瀏覽器渲染頁面默認採用的是流式布局模型(Flow Based Layout),這一點很重要。

所謂重排,實際上是根據渲染樹中每個渲染對象的信息,計算出各自渲染對象的幾何信息(DOM對象的位置和尺寸大小),並將其安置在界面中的正確位置。

由於瀏覽器渲染界面是基於流式布局模型的,也就是某一個DOM節點信息更改了,就需要對DOM結構進行重新計算,重新布局界面,再次引發迴流,只是這個結構更改程度會決定周邊DOM更改範圍,即全局範圍和局部範圍,全局範圍就是從根節點html開始對整個渲染樹進行重新布局,例如當我們改變了窗口尺寸或方向或者是修改了根元素的尺寸或者字體大小等;而局部布局可以是對渲染樹的某部分或某一個渲染對象進行重新布局。

在此,總結會引起重排的操作有:

  1. 頁面首次渲染。
  2. 瀏覽器窗口大小發生改變。
  3. 元素尺寸或位置發生改變。
  4. 元素內容變化(文字數量或圖片大小等等)。
  5. 元素字體大小變化。
  6. 添加或者刪除可見的DOM元素。
  7. 激活CSS偽類(例如::hover)。
  8. 設置style屬性
  9. 查詢某些屬性或調用某些方法。

重排也叫迴流,實際上,reflow的字面意思也是迴流,之所以有的叫做重排,也許是因為重排更好理解,更符合中國人的思維。標準文檔之所以叫做迴流(Reflow),是因為瀏覽器渲染是基於「流式布局」的模型,流實際就使我們常說的文檔流,當dom或者css幾何屬性發生改變的時候,文檔流會受到波動聯動的去更改,流就好比一條河裡的水,迴流就好比向河裡扔了一塊石頭,激起漣漪,然後引起周邊水流受到波及,所以叫做迴流,這樣理解似乎更標準更規範,不過叫什麼並不重要,重要的是我們真正理解了這個過程便好。

重繪(Repainting)

相比重排,重繪就簡單多了,所謂重繪,就是當頁面中元素樣式的改變並不影響它在文檔流中的位置時,例如更改了字體顏色,瀏覽器會將新樣式賦予給元素並重新繪製的過程稱。

常見引起瀏覽器繪製過程的屬性包含:

性能優化

我們知道操作DOM是一個高成本的操作,不僅是因為本身js與DOM的鏈接訪問,還包括操作DOM後悔引起一連串的連鎖反應(重排),因此,從性能優化角度,我們可以從以下幾個方面著手:

  • 減少DOM操作

    • 最小化DOM訪問次數,盡量緩存訪問DOM的樣式信息,避免過度觸發迴流。
    • 如果在一個局部方法中需要多次訪問同一個dom,則先暫存它的引用。
  • 採用更優的API替代消費高的api,轉換優化消費高的集合

    • 用querySelectorAll()替代getElementByXX()。
    • 開啟動畫的GPU加速,把渲染計算交給GPU。
    • 少用HTML集合(類數組)來遍歷,因為集合遍歷比真數組遍歷耗費更高。
    • 用事件委託來減少事件處理器的數量。
  • 減少重排

    • 避免設置大量的style屬性,因為通過設置style屬性改變結點樣式的話,每一次設置都會觸發一次reflow,所以最好是使用class屬性
    • 實現元素的動畫,它的position屬性,最好是設為absoulte或fixed,這樣不會影響其他元素的布局
    • 動畫實現的速度的選擇。比如實現一個動畫,以1個像素為單位移動這樣最平滑,但是reflow就會過於頻繁,大量消耗CPU資源,如果以3個像素為單位移動則會好很多。
    • 不要使用table布局,因為table中某個元素旦觸發了reflow,那麼整個table的元素都會觸發reflow。那麼在不得已使用table的場合,可以設置table-layout:auto;或者是table-layout:fixed這樣可以讓table一行一行的渲染,這種做法也是為了限制reflow的影響範圍
  • css及動畫處理

    • 少用css表達式
    • 減少通過JavaScript代碼修改元素樣式,盡量使用修改class名方式操作樣式或動畫;
    • 動畫盡量使用在絕對定位或固定定位的元素上;
    • 隱藏在屏幕外,或在頁面滾動時,盡量停止動畫;

最後總結

本篇文章主要抓取url從輸入到最後渲染成界面這一流程中的瀏覽器解析渲染HTML這一步驟來探討前端優化的思路和原因,核心思想基於重排和重繪的關係來展開討論,主題大致有如下幾點:

  • url從輸入到最後渲染的大致環節。
  • 重排一定會重繪,重繪不一定有重排。
  • Js操作DOM是一個高消費過程。
  • 會引起重排/重繪的屬性和方法列舉
  • 優化思路(減少dom操作、替換高性能api、暫存引用、減少重排、開啟硬體加速等)。

最後,由於個人水平原因,若有行文不全或疏漏錯誤之處,懇請各位讀者批評指正,一路有你,不勝感激!

感謝這個時代,讓我們可以站在巨人的肩膀上,窺探程序世界的宏偉壯觀,我願以一顆赤子心,踏遍程序世界的千山萬水!願每一個行走在程序世界的同仁,都活成心中想要的樣子,加油

作者:小白師兄

鏈接:imooc.com/article/detai

來源:慕課網

本文原創發佈於慕課網 ,轉載請註明出處,謝謝合作


推薦閱讀:

【官方】手記欄目認證作者招募,長期有效,隨時報名!_慕課手記

有獎徵文006期 | 聊聊面試那些事兒?

30行Javascript代碼實現圖片懶載入

PWA,現代前端必會的黑科技

HTML_關於現代前端必要知識


推薦閱讀:

埋點實現原理了解一下
給愛看書的你
《高效前端:Web高效編程與優化實踐》送書活動!
技術周刊(計算機視覺 2018-07-23)
努力讓自己更好

TAG:前端性能優化 | 網頁瀏覽器 | 前端開發 |