聽說有個能優化性能的屬性 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 size
  • content:layout style paint

適用場景

  1. 元素在屏幕外不可見時
  2. 第三方插件
  3. 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 文章中的效果:

credit to Paul Lewis article

實驗內容就是,一個從上到下排列的頁面結構,在中間顏色為黃色的節點內不斷插入新的子節點,將會觸發重排:

多次試驗後的結果如下:

二者區別在於黃色節點有沒有 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

結論是,在會引起重排的場景下,可以有效縮小重排的範圍。

兼容性

參考資料

  1. CSS containment
  2. container-queries-issue3
  3. W3C - containment
  4. MDN - contain
  5. CSS Containment in Chrome 52

最後 ( ̄︶ ̄)/

寫完這篇發現最大的寶藏,應該是 Chrome DevTools。即使只有老方案,也不代表所有問題都被解決了,很多問題可能只是尚且沒被發現吧


推薦閱讀:

單頁web應用如何作性能檢測?
如何解決前端遇到的兼容問題?
「每日一題」你是如何做性能優化的?
一道滴滴的負載均衡前端面試題
PWA 漸進式實踐 (2) - Service Worker

TAG:前端开发 | CSS | 前端性能优化 |