爲何操做DOM會影響WEB應用的性能?

面試官常常會問你:「平時工做中,你怎麼優化本身應用的性能?」
你回答以下:「我平時遵循如下幾條原則來優化個人項目、以提升性能,主要有:」javascript

a. 減小DOM操做的次數(減小DOM的獲取與修改次數)css

b. 減小網絡請求html

c. 壓縮、合併靜態資源文件(css、js、img等)前端

d. 小圖片文件base64化處理vue

e. js少用全局變量java

f. ...web

Bingo!此時,你給本身刨了個能夠把本身埋住的大坑。
由於面試官可能會追問你:「爲何減小DOM操做能夠提升性能?」面試

爲何呢?
_______chrome

一、dom是什麼?ES和 DOM是什麼關係?

DOM就是Document Object Model,文檔對象模型,裏邊是接口,即方法函數。咱們經過調用並傳指定參數來使用。
官方定義:DOM是一個獨立於語言的、用於操做XML和HTML文檔的程序接口(API)。在瀏覽器中主要用於與HTML文檔打交道,而且使用DOM API用來訪問文檔中的數據。
DOM是個與ES語言無關的API,它在瀏覽器中的接口倒是用JavaScript來實現的,DOM就成了如今JS編碼中的重要部分。瀏覽器

1-一、各大瀏覽器中,DOM的位置和JavaScript的位置(渲染引擎與JS引擎相互獨立)

瀏覽器 JS位置 DOM位置
IE JavaScript的實現名爲JScript,位於jscript.dll文件中 DOM的實現則存在另外一個庫中,名爲mshtml.dll(內部稱爲trident)
safari JavaScript部分是由獨立的SquirelFish引擎來實現。 DOM和渲染是使用webkit中的webcore實現
google chrome JavaScript引擎是他們本身研發的,名爲V8。 使用webkit中的webCore庫來渲染頁面
firefox JavaScript引擎名爲TraceMonkey 渲染引擎Gecko

1-二、ES和 DOM是兩種東西

ES經過DOM接口來獲取文檔中的元素。
正由於瀏覽器中一般把DOM和ECMAScript獨立實現。使得兩者相互獨立,就像兩座孤島。
因此ES每次操做DOM時,ES和DOM之間就像兩個橋之間須要過車輛。
每次連接就都須要搭建一個橋樑,搭橋仍是小事,ES請求DOM的車輛過橋時,會通過一個收費站,每次都會被收費。JS引擎會消耗瀏覽器的性能進行繳費。
而車輛經過後橋就銷燬,下次連接從新搭橋二次繳費。因此說JS與DOM每次鏈接都須要消耗性能
也正所以,有了每操做一次DOM就多作點事的理念,儘量以最少的次數處理最多的DOM操做,以實現每過一次橋多拉點貨的效果。
(VUE也正是這種理念,操做虛擬dom減小性能消耗,所以vue性能更優,另個話題來講。)

二、ES每次訪問DOM都須要消耗性能:

正由於兩者相互獨立,因此每次連接、每次訪問DOM都會消耗性能!! 能夠說操做dom是十分昂貴的!!寧肯處理一萬次js,也不操做一次dom!!

三、ES每次修改DOM元素的代價則更爲昂貴

像上邊說的,每次操做DOM以前,就會先訪問DOM,因此也會消耗性能。
在此基礎上,由於修改DOM會致使瀏覽器從新計算頁面的幾何變化、引起瀏覽器模板引擎的重排(迴流 - 回滾流程)和重繪,進而更加消耗性能。

四、瀏覽器渲染引擎的工做原理、工做流程是什麼?

瀏覽器下載完頁面中的全部資源(好比HTML、JavaScript、CSS、圖片等)後,會發生以下的6步過程:

  1. 解析HTML,構建DOM樹(DOM Tree)
  2. 解析CSS,生成CSS規則樹(CSSOM Tree)
  3. 合併DOM樹和CSS規則樹,生成渲染樹render樹(render Tree)
  4. 佈局render樹,根據生成的render樹來對各元素尺寸、位置進行計算,獲得每一個節點的幾何信息。(根據視口的大小來計算元素的位置和大小)(重排會走這一步)
  5. 繪製render樹,繪製頁面像素信息(根據render樹上每一個節點的幾何信息,獲得每一個節點的像素數)(重繪會走這一步)
  6. 瀏覽器會將各層節點的像素信息發送給GPU,GPU將各層合成、繪製展現到頁面上

4-一、瀏覽器渲染引擎是如何生成渲染樹(render Tree)的?

先看一張圖:
瀏覽器如何生成渲染樹

由上圖得知以下流程:

  1. 從DOM Tree的根節點開始遍歷每個可見節點(除meta、link、script等這些標籤;除display:none;的元素)
  2. 對於每一個可見節點,在CSSOM中找到對應規則並將樣式規則應用到對應節點上。
  3. 根據每個可見節點,以及其對應的樣式,組合生成渲染樹。

不可見節點: 不會渲染輸出的節點(不會顯示在屏幕上的節點)有如下幾種

  • meta、link、script等標籤;
  • 經過css進行隱藏的節點,即display:none;(opacity對人類不可見,計算機還能看見,因此還會渲染。)(那visibility爲隱藏的元素會不會被渲染呢?作個試驗,一個div設置visibility不可見,左浮動,周圍全是文字,看文字環繞是否讓出一塊空白區域。最後試驗證實確實繞出了一段空白的位置,說明visibility和opacity設置的不可見只是對人類肉眼不可見,計算機仍是會在生成render Tree的時候計算位置信息並把他繪製出來。試驗結果以下圖:)

visibility爲隱藏的元素會不會被渲染

五、什麼是瀏覽器渲染引擎的重排和重繪?

5-一、重排

當DOM的變化影響了元素的幾何屬性(寬和高),瀏覽器須要從新計算元素的幾何屬性,一樣其餘相鄰元素的幾何屬性和位置也會所以受到影響。瀏覽器會使渲染樹中受到影響的部分失效,並從新構造渲染樹。這個過程稱爲「重排」。

換句話說,改變了頁面中某元素的位置、尺寸大小,進而也就改變了他的佔地面積。那這個元素修改了佔地面積後,其後緊鄰的元素就得挪動位置。給她讓地兒(或者向前趕趕)。緊鄰的元素挪動了,那緊鄰元素後邊的元素也會連鎖效應式的修改。這就比如一排人排隊。前邊的人忽然變胖了、變瘦了、向前挪了、向後擠了、都會致使隊伍中後邊的人也跟隨之改變位置,由此致使一連串的人都挪動位置。這時瀏覽器就要從新排版各個受到影響的元素的位置。反應在渲染引擎的工做流程中也就是瀏覽器須要從新計算元素位置信息並佈局render樹。這就是重排

5-二、重繪

完成重排後,瀏覽器會從新繪製受影響的部分到屏幕中,該過程稱爲重繪
由於重排在重繪的上一步,因此重排發生後天然會致使重繪。這個很好理解。

六、何時會引起重排?

當頁面佈局和幾何屬性改變時就須要重排:
(核心就是:只要某個屬性能致使位置信息發生改變,就會觸發重排 )

  1. 添加或刪除可見的DOM元素。(一堆人排隊,添加即中間插入了一我的/刪除即中間一我的走了,勢必會影響後邊排隊的人的位置信息也發生改變)
  2. 元素位置改變(重排就是由於位置信息改變了)
  3. 元素尺寸改變( 外邊距、內邊距、邊框厚度、寬度、高度等)
  4. 內容改變,例:文本數量/內容改變、或圖片被另外一個不一樣尺寸的圖片替代、字體大小改變、(文字加粗?)致使DOM元素位置、面積改變。【計算會消耗CPU的能力】
  5. 頁面渲染器初始化(這算重走流程吧,確定要重排)
  6. 瀏覽器窗口尺寸改變(位置信息會被迫調整,發生重排。見下圖的gif圖,一個頁面中div元素的位置不受視口調整而修改,也會引起重排)【消耗GPU的計算能力】

試驗:resize視口,一個頁面中div元素的位置不受視口調整而修改,也會引起重排
resize視口會引起重排

七、打斷瀏覽器的優化步驟

現代瀏覽器是至關完善的了,由於屢次操做DOM會觸發重排重繪、消耗性能。因此除了咱們人爲的、有意識的去控制操做DOM次數之外,瀏覽器在設計上進行了優化,也會智能的「節流」操做DOM,好比實現隊列化修改、批量執行。

解釋來講就是,瀏覽器會有一個「隊列」,用以存放(攢着)須要操做DOM的js程序。每當執行一次js操做dom的代碼,這個隊列裏就先暫存一個程序。等到一段時間後,瀏覽器再集中、批量的連接一次"ES島"和"DOM島"(就是讓JS引擎去連接渲染引擎),進而觸發一次DOM操做。你能夠形象的理解爲「過一段時間發一班車」。

可是咱們人類感知不到啊,可能會由於誤操做打斷瀏覽器的「節流」步驟。迫使瀏覽器中斷當前的「等待」,去趕忙、立馬進行一次dom操做。讓瀏覽器趕忙執行完他攢在「隊列」裏的JS操做DOM的程序後返回最新的DOM位置信息給咱們。這就好像電梯門定時自動關閉,可是你卻手動按了關門按鈕強迫關門同樣。

這種狀況就發生在咱們獲取DOM信息的時候:

打斷瀏覽器優化,強迫觸發重排的屬性:
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
getComputedStyle()

由於要跟瀏覽器請求最新的DOM信息,因此瀏覽器就得趕忙讓JS引擎去渲染引擎那裏進行一次DOM操做。

八、何時會引起重繪?

  1. 重排必然引起重繪,這是確定的。由於瀏覽器的工做流程就是排版後渲染。重排會迴流(回滾流程)到排版階段,排版後須要從新繪製頁面。
  2. 單獨觸發重繪的狀況:
    除元素尺寸、位置發生改變之外的狀況,(好比字體顏色、背景色等發生改變)。(我懷疑文字加粗也會觸發重排,可是我沒有證據。理論上來講若是在一個固定尺寸的div內加粗文字,應該不會影響後邊元素的重排,但可能該div內部的其餘相鄰文字或元素會發生重排。)

試驗gif圖:
文字加粗會引起重排

(想到一個驗證只發生重繪的狀況,那就是後邊也加點元素,若是重排了,後邊的元素在控制檯的檢測下也會閃綠光。)

九、爲何不提倡重排和重繪?

既然知道了這個dom操做會觸發重排、重繪。那又是爲何要儘可能避免重排和重繪呢?
換句話說,重排和重繪的反作用是什麼?缺點是什麼?

這就要引入CPU和GPU了。

重排會佔用CPU,dom元素位置計算會消耗CPU的算力,因此應該儘可能減小CPU的佔用,使電腦不卡頓。
重繪會佔用GPU,渲染頁面時會消耗GPU的算力。

GPU的分類:

  1. 家用GPU
    適合作貼圖、特效、光影等效果。不適合畫圖形。
  2. 專業GPU
    適合畫圖形。不適合作貼圖、特效、光影等效果。

DOM操做基本就是畫圖形的,但瀏覽器中用的就是家用GPU,其畫圖形耗費的性能是專業GPU的幾十倍。因此不提倡頻繁用裝有家用GPU的瀏覽器繪製頁面。也就是不提倡頻繁觸發重繪。

十、總結: 爲何操做DOM很是昂貴?

  1. ES和 DOM是兩種東西,每次鏈接都須要消耗性能
  2. 操做DOM會致使重排和重繪,重排會佔用、消耗CPU; 重繪會佔用、消耗GPU

十一、控制檯觀察一個頁面的重排和重繪現象

由於重排必然會引起重繪,因此在瀏覽器的開發者工具中提供了一個檢測重繪的按鈕。尋找和打開步驟以下圖:
控制檯開啓重繪檢測按鈕

各css屬性對重排重繪的影響:https://csstriggers.com/

能夠關注個人微信公衆號看更多總結文章~
前端說吧

相關文章
相關標籤/搜索