摘要:渲染層合并對頁面中元素的繪制是在多個(gè)層上進(jìn)行的。擁有兩套不同的渲染路徑硬件加速路徑和舊軟件路徑中有不同類型的層負(fù)責(zé)子樹和負(fù)責(zé)的子樹,只有是作為紋理上傳給的。整個(gè)圖在中其實(shí)有幾種不同的層類型渲染層,這是負(fù)責(zé)對應(yīng)子樹圖形層,這是負(fù)責(zé)對應(yīng)子樹。
梳理瀏覽器渲染流程
首先簡單了解一下瀏覽器請求、加載、渲染一個(gè)頁面的大致過程:
DNS 查詢
TCP 連接
HTTP 請求即響應(yīng)
服務(wù)器響應(yīng)
客戶端渲染
這里主要將客戶端渲染展開梳理一下,從瀏覽器器內(nèi)核拿到內(nèi)容(渲染線程接收請求,加載網(wǎng)頁并渲染網(wǎng)頁),渲染大概可以劃分成以下幾個(gè)步驟:
解析html建立dom樹
解析css構(gòu)建render樹(將CSS代碼解析成樹形的數(shù)據(jù)結(jié)構(gòu),然后結(jié)合DOM合并成render樹)
布局render樹(Layout/reflow),負(fù)責(zé)各元素尺寸、位置的計(jì)算
繪制render樹(paint),繪制頁面像素信息
瀏覽器會(huì)將各層的信息發(fā)送給GPU(GPU進(jìn)程:最多一個(gè),用于3D繪制等),GPU會(huì)將各層合成(composite),顯示在屏幕上。
參考一張圖(webkit渲染主要流程):
這里先解釋一下幾個(gè)概念,方便大家理解:
DOM Tree:瀏覽器將HTML解析成樹形的數(shù)據(jù)結(jié)構(gòu)。
CSS Rule Tree:瀏覽器將CSS解析成樹形的數(shù)據(jù)結(jié)構(gòu)。
Render Tree: DOM和CSSOM合并后生成Render Tree。
layout: 有了Render Tree,瀏覽器已經(jīng)能知道網(wǎng)頁中有哪些節(jié)點(diǎn)、各個(gè)節(jié)點(diǎn)的CSS定義以及他們的從屬關(guān)系,從而去計(jì)算出每個(gè)節(jié)點(diǎn)在屏幕中的位置。
painting: 按照算出來的規(guī)則,通過顯卡,把內(nèi)容畫到屏幕上。
reflow(回流):當(dāng)瀏覽器發(fā)現(xiàn)某個(gè)部分發(fā)生了點(diǎn)變化影響了布局,需要倒回去重新渲染,內(nèi)行稱這個(gè)回退的過程叫 reflow。reflow 會(huì)從 這個(gè) root frame 開始遞歸往下,依次計(jì)算所有的結(jié)點(diǎn)幾何尺寸和位置。reflow 幾乎是無法避免的。現(xiàn)在界面上流行的一些效果,比如樹狀目錄的折疊、展開(實(shí)質(zhì)上是元素的顯 示與隱藏)等,都將引起瀏覽器的 reflow。鼠標(biāo)滑過、點(diǎn)擊……只要這些行為引起了頁面上某些元素的占位面積、定位方式、邊距等屬性的變化,都會(huì)引起它內(nèi)部、周圍甚至整個(gè)頁面的重新渲 染。通常我們都無法預(yù)估瀏覽器到底會(huì) reflow 哪一部分的代碼,它們都彼此相互影響著。
repaint(重繪):改變某個(gè)元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內(nèi)部布局的屬性時(shí),屏幕的一部分要重畫,但是元素的幾何尺寸沒有變。
注意:
display:none 的節(jié)點(diǎn)不會(huì)被加入Render Tree,而visibility: hidden
則會(huì),所以,如果某個(gè)節(jié)點(diǎn)最開始是不顯示的,設(shè)為display:none是更優(yōu)的。
display:none 會(huì)觸發(fā) reflow,而 visibility:hidden 只會(huì)觸發(fā) repaint,因?yàn)闆]有發(fā)現(xiàn)位置變化。
有些情況下,比如修改了元素的樣式,瀏覽器并不會(huì)立刻reflow 或 repaint 一次,而是會(huì)把這樣的操作積攢一批,然后做一次reflow,這又叫異步 reflow 或增量異步 reflow。但是在有些情況下,比如resize窗口,改變了頁面默認(rèn)的字體等。對于這些操作,瀏覽器會(huì)馬上進(jìn)行 reflow。
再參考一張圖理解一下:
細(xì)致分離兩個(gè)環(huán)節(jié),其他環(huán)節(jié)參考上述概念注解:
JavaScript:JavaScript實(shí)現(xiàn)動(dòng)畫效果,DOM元素操作等。
Composite(渲染層合并):對頁面中 DOM 元素的繪制是在多個(gè)層上進(jìn)行的。在每個(gè)層上完成繪制過程之后,瀏覽器會(huì)將所有層按照合理的順序合并成一個(gè)圖層,然后顯示在屏幕上。對于有位置重疊的元素的頁面,這個(gè)過程尤其重要,因?yàn)橐坏﹫D層的合并順序出錯(cuò),將會(huì)導(dǎo)致元素顯示異常。
在實(shí)際場景下,大致會(huì)出現(xiàn)三種常見的渲染流程(Layout和Paint步驟是可避免的,可參考上一張圖的注意部分理解):
注意:首先說明,這里討論的是 WebKit,描述的是 Chrome 的實(shí)現(xiàn)細(xì)節(jié),而并非是 web 平臺的功能,因此這里介紹的內(nèi)容不一定適用于其他瀏覽器。
Chrome 擁有兩套不同的渲染路徑(rendering path):硬件加速路徑和舊軟件路徑(older software path)
Chrome 中有不同類型的層: RenderLayer(負(fù)責(zé) DOM 子樹)和GraphicsLayer(負(fù)責(zé) RenderLayer的子樹),只有 GraphicsLayer 是作為紋理(texture)上傳給GPU的。
什么是紋理?可以把它想象成一個(gè)從主存儲器(例如 RAM)移動(dòng)到圖像存儲器(例如 GPU 中的 VRAM)的位圖圖像(bitmapimage)
Chrome 使用紋理來從 GPU上獲得大塊的頁面內(nèi)容。通過將紋理應(yīng)用到一個(gè)非常簡單的矩形網(wǎng)格就能很容易匹配不同的位置(position)和變形(transformation)。這也就是3DCSS 的工作原理,它對于快速滾動(dòng)也十分有效。
整個(gè)圖:
在 Chrome 中其實(shí)有幾種不同的層類型:
RenderLayers 渲染層,這是負(fù)責(zé)對應(yīng) DOM 子樹
GraphicsLayers 圖形層,這是負(fù)責(zé)對應(yīng) RenderLayers子樹。
在瀏覽器渲染流程中提到了composite概念,在 DOM 樹中每個(gè)節(jié)點(diǎn)都會(huì)對應(yīng)一個(gè) LayoutObject,當(dāng)他們的 LayoutObject 處于相同的坐標(biāo)空間時(shí),就會(huì)形成一個(gè) RenderLayers ,也就是渲染層。RenderLayers 來保證頁面元素以正確的順序合成,這時(shí)候就會(huì)出現(xiàn)層合成(composite),從而正確處理透明元素和重疊元素的顯示。
某些特殊的渲染層會(huì)被認(rèn)為是合成層(Compositing Layers),合成層擁有多帶帶的 GraphicsLayer,而其他不是合成層的渲染層,則和其第一個(gè)擁有 GraphicsLayer 父層公用一個(gè)。
而每個(gè)GraphicsLayer(合成層多帶帶擁有的圖層) 都有一個(gè) GraphicsContext,GraphicsContext 會(huì)負(fù)責(zé)輸出該層的位圖,位圖是存儲在共享內(nèi)存中,作為紋理上傳到 GPU 中,最后由 GPU 將多個(gè)位圖進(jìn)行合成,然后顯示到屏幕上。
如何變成合成層合成層創(chuàng)建標(biāo)準(zhǔn)合成層的優(yōu)點(diǎn)什么情況下能使元素獲得自己的層?雖然 Chrome的啟發(fā)式方法(heuristic)隨著時(shí)間在不斷發(fā)展進(jìn)步,但是從目前來說,滿足以下任意情況便會(huì)創(chuàng)建層:
3D 或透視變換(perspective transform) CSS 屬性
使用加速視頻解碼的
(WebGL) 上下文或加速的 2D 上下文的
混合插件(如 Flash)
對自己的 opacity 做 CSS動(dòng)畫或使用一個(gè)動(dòng)畫變換的元素
擁有加速 CSS 過濾器的元素
元素有一個(gè)包含復(fù)合層的后代節(jié)點(diǎn)(換句話說,就是一個(gè)元素?fù)碛幸粋€(gè)子元素,該子元素在自己的層里)
元素有一個(gè)z-index較低且包含一個(gè)復(fù)合層的兄弟元素(換句話說就是該元素在復(fù)合層上面渲染)
淘寶的栗子舉的很詳細(xì),值得一看,里面提到了一旦renderLayer提升為了合成層就會(huì)有自己的繪圖上下文,并且會(huì)開啟硬件加速,有利于性能提升,里面列舉了一些特點(diǎn)
合成層的位圖,會(huì)交由 GPU 合成,比 CPU 處理要快
當(dāng)需要 repaint 時(shí),只需要 repaint 本身,不會(huì)影響到其他的層
對于 transform 和 opacity 效果,不會(huì)觸發(fā) layout 和 paint
注意:
提升到合成層后合成層的位圖會(huì)交GPU處理,但請注意,僅僅只是合成的處理(把繪圖上下文的位圖輸出進(jìn)行組合)需要用到GPU,生成合成層的位圖處理(繪圖上下文的工作)是需要CPU。
當(dāng)需要repaint的時(shí)候可以只repaint本身,不影響其他層,但是paint之前還有style, layout,那就意味著即使合成層只是repaint了自己,但style和layout本身就很占用時(shí)間。
僅僅是transform和opacity不會(huì)引發(fā)layout 和paint,那么其他的屬性不確定。
總結(jié)合成層的優(yōu)勢:一般一個(gè)元素開啟硬件加速后會(huì)變成合成層,可以獨(dú)立于普通文檔流中,改動(dòng)后可以避免整個(gè)頁面重繪,提升性能。
性能優(yōu)化點(diǎn):
提升動(dòng)畫效果的元素 合成層的好處是不會(huì)影響到其他元素的繪制,因此,為了減少動(dòng)畫元素對其他元素的影響,從而減少paint,我們需要把動(dòng)畫效果中的元素提升為合成層。 提升合成層的最好方式是使用 CSS 的 will-change屬性。從上一節(jié)合成層產(chǎn)生原因中,可以知道 will-change 設(shè)置為opacity、transform、top、left、bottom、right 可以將元素提升為合成層。
使用 transform 或者 opacity 來實(shí)現(xiàn)動(dòng)畫效果, 這樣只需要做合成層的合并就好了。
減少繪制區(qū)域 對于不需要重新繪制的區(qū)域應(yīng)盡量避免繪制,以減少繪制區(qū)域,比如一個(gè) fix 在頁面頂部的固定不變的導(dǎo)航header,在頁面內(nèi)容某個(gè)區(qū)域 repaint 時(shí),整個(gè)屏幕包括 fix 的 header 也會(huì)被重繪。而對于固定不變的區(qū)域,我們期望其并不會(huì)被重繪,因此可以通過之前的方法,將其提升為獨(dú)立的合成層。減少繪制區(qū)域,需要仔細(xì)分析頁面,區(qū)分繪制區(qū)域,減少重繪區(qū)域甚至避免重繪。
利用合成層可能踩到的坑合成層占用內(nèi)存的問題
層爆炸,由于某些原因可能導(dǎo)致產(chǎn)生大量不在預(yù)期內(nèi)的合成層,雖然有瀏覽器的層壓縮機(jī)制,但是也有很多無法進(jìn)行壓縮的情況,這就可能出現(xiàn)層爆炸的現(xiàn)象(簡單理解就是,很多不需要提升為合成層的元素因?yàn)槟承┎划?dāng)操作成為了合成層)。解決層爆炸的問題,最佳方案是打破 overlap 的條件,也就是說讓其他元素不要和合成層元素重疊。簡單直接的方式:使用3D硬件加速提升動(dòng)畫性能時(shí),最好給元素增加一個(gè)z-index屬性,人為干擾合成的排序,可以有效減少chrome創(chuàng)建不必要的合成層,提升渲染性能,移動(dòng)端優(yōu)化效果尤為明顯。 在這篇文章中的demo可以看出其中厲害。
用chremo打開demo頁面后,開啟瀏覽器的開發(fā)者模式,再按照如圖操作打開查看工具:
開啟 Rendering 的Layer borders后 觀察點(diǎn)擊為動(dòng)畫元素設(shè)置z-index復(fù)選框的頁面提示變化:
上圖中可以明顯看出:頁面中設(shè)置了一個(gè)h1標(biāo)題,應(yīng)用了translate3d動(dòng)畫,使得它被放到composited layer中渲染,然后在這個(gè)元素后面創(chuàng)建了2000個(gè)list。在不為h1元素設(shè)置z-index的情況下,使得本不需要提升到合成層的ul元素下的每個(gè)li元素都提升為一個(gè)多帶帶合成層(每個(gè)li元素的黃色提示邊框),最終會(huì)導(dǎo)致GPU資源過度消耗頁面滑動(dòng)時(shí)很卡,尤其在移動(dòng)端(安卓)上更加明顯。
如上圖操作選中為動(dòng)畫元素設(shè)置z-index,可以看出ul下的每個(gè)li都回歸到普通渲染層,不再是合成層也就不會(huì)消耗GPU資源去渲染,從而達(dá)到了優(yōu)化頁面性能優(yōu)化的目的。
大家可以用支持『硬件加速』的『安卓』手機(jī)瀏覽器測試上述頁面,給動(dòng)畫元素加z-index前后的性能差距非常明顯。
最后在實(shí)際的前端開發(fā)中尤其是移動(dòng)端開發(fā),很多小伙伴都很喜歡使用類似 translateZ(0)等屬性來進(jìn)行所謂的硬件加速,以提升性能,達(dá)到優(yōu)化頁面動(dòng)態(tài)效果的目的,但還是要注意凡事過猶不及,應(yīng)用硬件加速的同時(shí)也要注意到千萬別踩坑。
關(guān)于合成層的更細(xì)致具體的講解,可以仔細(xì)學(xué)習(xí)下下面的參考文章(尤其是前三篇哦)。
最后祝愿熱愛技術(shù)的你我始終堅(jiān)持在探索技術(shù)的路上奮力前行!
參考文章:
無線性能優(yōu)化:Composite
DOM to Screen
CSS GPU Animation: Doing It Right
web優(yōu)化之composite
詳談層合成(composite)
CSS3硬件加速也有坑
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/52230.html
摘要:渲染層合并對頁面中元素的繪制是在多個(gè)層上進(jìn)行的。擁有兩套不同的渲染路徑硬件加速路徑和舊軟件路徑中有不同類型的層負(fù)責(zé)子樹和負(fù)責(zé)的子樹,只有是作為紋理上傳給的。整個(gè)圖在中其實(shí)有幾種不同的層類型渲染層,這是負(fù)責(zé)對應(yīng)子樹圖形層,這是負(fù)責(zé)對應(yīng)子樹。 梳理瀏覽器渲染流程 首先簡單了解一下瀏覽器請求、加載、渲染一個(gè)頁面的大致過程: DNS 查詢 TCP 連接 HTTP 請求即響應(yīng) 服務(wù)器響應(yīng) 客戶...
摘要:渲染層合并對頁面中元素的繪制是在多個(gè)層上進(jìn)行的。擁有兩套不同的渲染路徑硬件加速路徑和舊軟件路徑中有不同類型的層負(fù)責(zé)子樹和負(fù)責(zé)的子樹,只有是作為紋理上傳給的。整個(gè)圖在中其實(shí)有幾種不同的層類型渲染層,這是負(fù)責(zé)對應(yīng)子樹圖形層,這是負(fù)責(zé)對應(yīng)子樹。 梳理瀏覽器渲染流程 首先簡單了解一下瀏覽器請求、加載、渲染一個(gè)頁面的大致過程: DNS 查詢 TCP 連接 HTTP 請求即響應(yīng) 服務(wù)器響應(yīng) 客戶...
摘要:三種渲染流程實(shí)際場景下,大概會(huì)有三種常見的渲染流程注意和步驟是可避免的優(yōu)化瀏覽器會(huì)在和加載完開始渲染頁面。優(yōu)化避免阻塞解析器通過以上兩種方式引入均會(huì)阻塞,因而會(huì)阻塞出現(xiàn)在腳本后面的標(biāo)記的渲染。 瀏覽器渲染原理 showImg(https://segmentfault.com/img/remote/1460000009159494?w=538&h=507);showImg(https:/...
摘要:但是還是會(huì)阻塞事件,所以會(huì)可能在觸發(fā)前或后執(zhí)行,但是一定會(huì)在事件前觸發(fā)。當(dāng)監(jiān)聽到該圖片元素進(jìn)入可視窗口時(shí),即將自定義屬性中的地址存儲到屬性中,達(dá)到懶加載的效果。當(dāng)代碼執(zhí)行,線程被凍結(jié)。所以的性能讓變慢。 概括 涉及到的分類 網(wǎng)絡(luò)層面 構(gòu)建層面 瀏覽器渲染層面 服務(wù)端層面 涉及到的功能點(diǎn) 資源的合并與壓縮 圖片編解碼原理和類型選擇 瀏覽器渲染機(jī)制 懶加載預(yù)加載 瀏覽器存儲 緩存機(jī)制...
閱讀 3590·2023-04-25 20:41
閱讀 2676·2023-04-25 16:40
閱讀 1446·2021-09-23 11:44
閱讀 1265·2021-09-10 10:51
閱讀 1695·2021-09-07 09:59
閱讀 1681·2019-12-27 12:08
閱讀 574·2019-08-30 15:44
閱讀 3346·2019-08-30 11:08