使用 CSS 包含屬性幫助瀏覽器進行優化
已發表: 2022-03-10contain
屬性為您提供了一種向瀏覽器解釋佈局的方法,因此可以進行性能優化。 但是,它確實會在您的佈局方面產生一些副作用。 在本文中,我將介紹一個剛剛成為 W3C 推薦標準的 CSS 規範。 CSS contain
規範定義了一個屬性, contains ,它可以幫助您向瀏覽器解釋佈局的哪些部分是獨立的,並且如果佈局的其他部分發生更改,則不需要重新計算。
雖然存在此屬性是出於性能優化的原因,但它也會影響頁面的佈局。 因此,在本文中,我將解釋您可以從中受益的不同類型的包含,以及在將contain
應用於站點中的元素時需要注意的事項。
佈局重新計算的問題
如果您正在構建簡單的網頁,在使用 JavaScript 加載元素後不會動態添加或更改元素,那麼您無需擔心 CSS Containment 解決的問題。 加載頁面時,瀏覽器只需要計算一次佈局。
當您想在頁面中添加元素而不需要用戶重新加載它時,包含變得有用的地方。 在我的示例中,我創建了一個大事件列表。 如果單擊按鈕,則修改第一個事件,添加一個浮動元素,並更改文本:

當我們的盒子的內容髮生變化時,瀏覽器必須考慮到任何元素都可能發生了變化。 瀏覽器通常很擅長處理這個問題,因為這是經常發生的事情。 也就是說,作為開發人員,您將知道每個組件是否是獨立的,並且對一個組件的更改不會影響其他組件,因此如果您可以通過 CSS 讓瀏覽器知道這一點,那就太好了。 這就是包含和 CSS contain
屬性為您提供的內容。
遏制如何提供幫助?
HTML 文檔是一種樹形結構,您可以在使用 DevTools 檢查任何元素時看到它。 在上面的示例中,我確定了一項我想使用 JavaScript 更改的項目,然後對內部進行一些更改。 (這意味著我只更改該列表項的子樹內的內容。)

將contain
屬性應用於一個元素會告訴瀏覽器,更改的範圍是該元素的子樹,以便瀏覽器可以進行任何可能的優化 - 安全地知道該元素之外的任何其他內容都不會改變。 特定瀏覽器可能會做什麼取決於引擎。 CSS 屬性只是讓你——作為這個佈局的開發者和專家——有機會讓它知道。
在許多情況下,您可以安全地繼續使用contain
屬性,但是,不同的值會帶來一些潛在的副作用,在將屬性添加到站點中的元素之前值得了解這些副作用。
使用遏制
contains 屬性可以設置三種不同類型的contain
:
-
layout
-
paint
-
size
注意: 2 級規範中有一個style
值。 它已從級別 1 中刪除,因此未出現在建議中,也未在 Firefox 中實現。
佈局
佈局遏制帶來了最大的好處。 要打開佈局包含,請使用以下代碼段:
.item { contain: layout; }
啟用佈局包含後,瀏覽器知道元素外部的任何內容都不會影響內部佈局,元素內部的任何內容都無法更改其外部事物的佈局。 這意味著它可以針對這種情況進行任何可能的優化。
啟用佈局包含時會發生一些額外的事情。 這些都是確保此框和內容獨立於樹的其餘部分的所有內容。
該框建立了一個獨立的格式化上下文。 這確保了盒子的內容保留在盒子中——特別是浮動將被包含在內,並且邊距不會在盒子中塌陷。 這與我們在使用display: flow-root
時打開的行為相同,如我的文章“理解 CSS 佈局和塊格式化上下文”中所述。 如果浮動可能會從您的盒子中伸出,導致後續文本在浮動周圍流動,那將是元素正在改變其外部事物的佈局的情況,使其成為包含的不良候選者。
包含框充當任何絕對或固定位置後代的包含塊。 這意味著它的行為就像您在已應用的框上使用了position: relative
一樣contain: layout
。
該框還創建了一個堆疊上下文。 因此z-index
將對這個元素起作用,它的子元素將基於這個新的上下文進行堆疊。
如果我們看一下這個例子,這次是contain: layout
,你可以看到當浮動元素被引入時,它不再伸出盒子的底部。 這是我們新的塊格式化上下文,包含浮動。

畫
要打開油漆遏制,請使用以下命令:

.item { contain: paint; }
啟用繪製包含後,會出現與佈局包含相同的副作用:包含框成為獨立的格式化上下文,定位元素的包含塊,並建立堆疊上下文。
繪製包含的作用是向瀏覽器指示包含塊內的元素在該框的邊界之外將不可見。 內容基本上將被剪輯到框中。
我們可以通過一個簡單的例子看到這種情況。 即使我們給我們的卡片一個高度,浮動的項目仍然會從盒子的底部伸出,因為浮動是不流動的。

啟用油漆控制後,浮動項目現在被裁剪為框的大小。 沒有任何東西可以在元素邊界之外繪製contain: paint
。

尺寸
如果您不完全了解它的工作原理,則尺寸限制是最有可能給您帶來問題的值。 要應用大小限制,請使用:
.item { contain: size; }
如果你使用尺寸限制,那麼你就是在告訴瀏覽器你知道盒子的尺寸並且它不會改變。 這確實意味著,如果您有一個在塊維度中自動調整大小的框,它將被視為內容沒有大小,因此框會像沒有內容一樣塌陷。
在下面的示例中,我沒有給li
指定高度; 它們還contain: size
。 您可以看到所有項目都已折疊,就好像它們根本沒有內容一樣,這使得列表看起來非常奇特!

如果你給盒子一個高度,那麼在使用contain: size
時高度將被尊重。 單獨而言,大小包含不會創建新的格式化上下文,因此不會像佈局和繪畫包含那樣包含浮動和邊距。 您不太可能單獨使用它; 相反,您很可能會將它與 contains 的其他值一起應用,以獲得最大可能的contain
。
速記值
在大多數情況下,您可以使用兩個速記值之一來獲得最佳的遏制效果。 要打開佈局和繪製包含,請使用contain: content;
, 並打開所有可能的包含(請記住,沒有大小的項目將折疊),請使用contain: strict
。
規範說:
“contain: content
可以合理地“安全”地廣泛應用; 它在實踐中的影響相當小,大多數內容都不會違反它的限制。 但是,因為它不應用大小限制,所以元素仍然可以響應其內容的大小,這可能導致佈局無效在樹上比預期的更遠。 盡可能使用contain: strict
來獲得盡可能多的遏制。”
因此,如果您事先不知道項目的大小,並且了解浮動和邊距將被包含的事實,請使用contain: content
。 如果您確實知道項目的大小並且對遏制的其他副作用感到滿意,請使用contain: strict
。 剩下的就是瀏覽器了,你已經解釋了你的佈局是如何工作的。
我現在可以使用收容措施嗎?
CSS Containment 規範現在是 W3C 推薦標準,我們有時將其稱為Web 標準。 為了使規范進入這個階段,我們需要在 Firefox 和 Chrome 中看到該功能的兩種實現:

由於此屬性對用戶是透明的,因此即使您在不支持它的瀏覽器中有大量訪問者,添加到任何站點也是完全安全的。 如果瀏覽器不支持遏制,那麼訪問者將獲得他們通常獲得的體驗,支持瀏覽器的訪問者將獲得增強的性能。
我建議添加到您在組件或模式庫中創建的任何組件中是一件很棒的事情,如果您以這種方式工作,那麼每個組件很可能被設計為一個獨立的東西,不會影響其他元素頁面,使contain: content
成為有用的補充。
因此,如果您有一個在加載後向 DOM 添加內容的頁面,我建議您嘗試一下——如果您得到任何有趣的結果,請在評論中告訴我!
相關資源
以下資源將為您提供有關遏制實施和潛在性能優勢的更多詳細信息:
- “
contain
CSS 屬性”, MDN 網絡文檔 - “Chrome 52 中的 CSS 包含”,谷歌開發者
- “CSS 包含模塊級別 1”, W3C 建議
- “CSS 遏制簡介”, Manuel Rego Casasnovas