聽說有個能優化性能的屬性 contain
延續譯文中「優化的未來」一節關於
contain
的討論。
TL;DR
contain
有性能優化作用,但限制較多,X5 兼容 iOS 不行。
屬性值列表
layout
:防止元素內 layout 改變影響元素外,也防止其他元素改變影響這個元素。paint
:元素的子元素必須在元素的 content-box 中,不能超出,否則顯示不出。子元素髮生的任何改變都不會影響到與該元素之外的其他元素;同樣該元素之外的其他元素都不會影響到子元素size
:用子元素是撐不開這個元素的(聲明都不給它的尺寸會一直是 0x0),必須聲明尺寸,且子元素不能超出元素的範圍,這個屬性能夠阻止子元素不斷變大 -> 改變父元素尺寸 -> 影響更多節點 -> 發成大面積重排。本身提供不了太大性能優化,一般是和layout
搭配使用。style
:有些 CSS 屬性會影響不只宿主元素和其子元素,比如counter
。為了限制這樣的屬性影響到別的元素,讓它的影響力限制在宿主元素和其子元素範圍內。強行生成一棵 DOM 子樹,變成像 shadow dom 那樣的情況,外面的變數不會影響裡面的;裡面的也不會影響到外面。strict
:layout style paint sizecontent
:layout style paint
適用場景
- 元素在屏幕外不可見時
- 第三方插件
- container queries
Use Case
注意:實驗環境都是 macOS 10.12.6 Chrome stable 61。demo 只是暫存 codepen,在 codepen 上運行相關代碼可能會得到與本文不同的實驗結果。
第三方插件和 cotainer queries 用得少,所以沒研究,只針對 off-screen 的情況寫了一些 demo。
case 1
If you build a off-screen navigation or similar, the browser paints the content completely although it is not visible on load. By setting
contain: paint;
the user agent can skip the paint off the off-screen element and therefore paint all the other content faster.
上文的意思是「如果構建一個屏幕外的導航欄(漢堡側邊欄),雖然看不到,但瀏覽器其實還會渲染那部分節點的。如果設置了 cotain: paint
那瀏覽器就不會去渲染屏幕外的東西,所以相對的屏幕內的內容就會被更快地渲染出來」。
為了驗證這個理論,將用 FMP (first meaningful page) 和首次可操作時間 (First interactive) 作為主要的衡量標準,畢竟對於用戶來說最有感知的應該也是這兩個特性了。推理過程是這樣的:
第一個頁面:側邊欄有一個高斯模糊的圖片,並動態加上了 1000 個高斯模糊的純色點;通過改變 left
值實現的移入移出視口。(實驗原則就是,怎麼慢怎麼來,滿足了自己的破壞欲= =)
點擊右上角「按鈕」,控制側邊欄的移動:
多次實驗後結果差不多是下面這樣(Chrome devTools 的 Audits 面板):
二者的區別是在側邊欄上有無 contain: paint
。需要特別注意的是,添加了 contain: paint
相當於給元素加了一個 position:relative; overflow: clip
,所以子元素會相對這個父元素來定位了:
aside {n position: absolute;n z-index: 1;n top: 0;n bottom: 0;n left: -101%;n transition: left 500ms ease;nn width: 80vw;n overflow: hidden; /* 為了和有 contain:paint 時的視覺效果一樣*/nn background-color: #414529;n background-image: url(http://qzonestyle.gtimg.cn/aoi/sola/20170707104843_SfolTLGT5s.jpg);n background-size: contain;n background-repeat: no-repeat;n filter: blur(10px);nn contain: paint;n}nnaside.open {n left: 0;n}n
結論:FMP 和 First Interactive 都有優化,其中 FMP 優化了40ms。
內存使用情況如下圖,這是多次試驗以後取了效果對比最明顯的:
case 2
按道理來說我們不應該看 FMP 而是應該看渲染的節點個數,但是因為側邊欄本身就是在複合層上,不參與 layout 時被影響的節點統計,通過開發者工具也沒有檢測到明顯的效果(從上圖中的 layout 可以看出)。所以寫了另一個 demo,用來驗證 Paul Lewis 文章中的效果:
實驗內容就是,一個從上到下排列的頁面結構,在中間顏色為黃色的節點內不斷插入新的子節點,將會觸發重排:
多次試驗後的結果如下:
二者區別在於黃色節點有沒有 contain: layout size
。另外 overflow: hidden
不會影響 layout root,但會影響 Nodes That Need Layout 這一欄。需要特別注意的是這兩個屬性值的使用場景,元素一定要有固定尺寸的。代碼如下:
article {n position: relative;n z-index: 1;n background-color: #FFE32A;nn /* no containment */n /* height: 200px;n * overflow: hidden;n */nn /* has containment */n height: 200px;n contain: layout size;n overflow: hidden;n}n
結論是,在會引起重排的場景下,可以有效縮小重排的範圍。
兼容性
參考資料
- CSS containment
- container-queries-issue3
- W3C - containment
- MDN - contain
- CSS Containment in Chrome 52
最後 ( ̄︶ ̄)/
寫完這篇發現最大的寶藏,應該是 Chrome DevTools。即使只有老方案,也不代表所有問題都被解決了,很多問題可能只是尚且沒被發現吧
推薦閱讀:
※單頁web應用如何作性能檢測?
※如何解決前端遇到的兼容問題?
※「每日一題」你是如何做性能優化的?
※一道滴滴的負載均衡前端面試題
※PWA 漸進式實踐 (2) - Service Worker