使用 content-visibility 優化渲染性能

来源:https://www.cnblogs.com/coco1s/archive/2022/06/14/16373817.html
-Advertisement-
Play Games

最近在業務中實際使用 content-visibility 進了一些渲染性能的優化。 這是一個比較新且有強大功能的屬性。本文將帶領大家深入理解一番。 何為 content-visibility? content-visibility:屬性控制一個元素是否渲染其內容,它允許用戶代理(瀏覽器)潛在地省略 ...


最近在業務中實際使用 content-visibility 進了一些渲染性能的優化。

這是一個比較新且有強大功能的屬性。本文將帶領大家深入理解一番。

何為 content-visibility

content-visibility:屬性控制一個元素是否渲染其內容,它允許用戶代理(瀏覽器)潛在地省略大量佈局和渲染工作,直到需要它為止。

MDN 原文:The content-visibility CSS property controls whether or not an element renders its contents at all, along with forcing a strong set of containments, allowing user agents to potentially omit large swathes of layout and rendering work until it becomes needed. Basically it enables the user agent to skip an element's rendering work (including layout and painting) until it is needed — which makes the initial page load much faster.

它有幾個常見的取值。

/* Keyword values */
content-visibility: visible;
content-visibility: hidden;
content-visibility: auto;

分別解釋一下:

  • content-visibility: visible:預設值,沒有任何效果,相當於沒有添加 content-visibility,元素的渲染與往常一致。
  • content-visibility: hidden:與 display: none 類似,用戶代理將跳過其內容的渲染。(這裡需要註意的是,跳過的是內容的渲染)
  • content-visibility: auto:如果該元素不在屏幕上,並且與用戶無關,則不會渲染其後代元素。

contain-intrinsic-size

當然,除 content-visibility 之外,還有一個與之配套的屬性 -- contain-intrinsic-size

contain-intrinsic-size:控制由 content-visibility 指定的元素的自然大小。

上面兩個屬性光看定義和介紹會有點繞。

我們首先來看看 content-visibility 如何具體使用。

content-visibility: visible 是預設值,添加後沒有任何效果,我們就直接跳過。

利用 content-visibility: hidden 優化展示切換性能

首先來看看 content-visibility: hidden,它通常會拿來和 display: none 做比較,但是其實它們之間還是有很大的不同的。

首先,假設我們有兩個 DIV 包裹框:

<div class="g-wrap">
    <div>1111</div>
    <div class="hidden">2222</div>
</div>

設置兩個 div 為 200x200 的黑色塊:

.g-wrap > div {
    width: 200px;
    height: 200px;
    background: #000;
}

效果如下:

OK,沒有問題,接下來,我們給其中的 .hidden 設置 content-visibility: hidden,看看會發生什麼:

.hidden {
    content-visibility: hidden;
}

效果如下:

註意,仔細看效果,這裡添加了 content-visibility: hidden 之後,消失的只是添加了該元素的 div 的子元素消失不見,而父元素本身及其樣式,還是存在頁面上的

如果我們去掉設置了 content-visibility: hidden 的元素本身的 widthheightpaddingmargin 等屬性,則元素看上去就如同設置了 display: none 一般,在頁面上消失不見了。

那麼,content-visibility: hidden 的作用是什麼呢?

設置了 content-visibility: hidden 的元素,其元素的子元素將被隱藏,但是,它的渲染狀態將會被緩存。所以,當 content-visibility: hidden 被移除時,用戶代理無需重頭開始渲染它和它的子元素。

因此,如果我們將這個屬性應用在一些一開始需要被隱藏,但是其後在頁面的某一時刻需要被渲染,或者是一些需要被頻繁切換顯示、隱藏狀態的元素上,其渲染效率將會有一個非常大的提升。

利用 content-visibility: auto 實現虛擬列表

OK,接下來是 content-visibility 的核心用法,利用 auto 屬性值。

content-visibility: auto 的作用是,如果該元素不在屏幕上,並且與用戶無關,則不會渲染其後代元素。是不是與 LazyLoad 非常類似?

我們來看這樣一個 DEMO ,瞭解其作用:

假設,我們存在這樣一個 HTML 結構,含有大量的文本內容:

<div class="g-wrap">
    <div class="paragraph">...</div>
    // ... 包含了 N 個 paragraph
    <div class="paragraph">...</div>
</div>

每個 .paragraph 的內容如下:

因此,整個的頁面看起來就是這樣的:

由於,我們沒有對頁面內容進行任何處理,因此,所有的 .paragraph 在頁面刷新的一瞬間,都會進行渲染,看到的效果就如上所示。

當然,現代瀏覽器愈加趨於智能,基於這種場景,其實我們非常希望對於仍未看到,仍舊未滾動到的區域,可以延遲載入,只有到我們需要展示、滾動到該處時,頁面內容才進行渲染。

基於這種場景,content-visibility: auto 就應運而生了,它允許瀏覽器對於設置了該屬性的元素進行判斷,如果該元素當前不處於視口內,則不渲染該元素。

我們基於上述的代碼,只需要最小化,添加這樣一段代碼:

.paragraph {
    content-visibility: auto;
}

再看看效果,仔細觀察右側的滾動條:

這裡我使用了 ::-webkit-scrollbar 相關樣式,讓滾動條更明顯。

可能你還沒意識到發生了什麼,我們對比下添加了 content-visibility: auto 和沒有添加 content-visibility: auto 的兩種效果下文本的整體高度:

有著非常明顯的差異,這是因為,設置了 content-visibility: auto 的元素,在非可視區域內,目前並沒有被渲染,因此,右側內容的高度其實是比正常狀態下少了一大截的。

好,我們實際開始進行滾動,看看會發生什麼:

由於下方的元素在滾動的過程中,出現在視口範圍內才被渲染,因此,滾動條出現了明顯的飄忽不定的抖動現象。(當然這也是使用了 content-visibility: auto 的一個小問題之一),不過明顯可以看出,這與我們通常使用 JavaScript 實現的虛擬列表非常類似。

當然,在向下滾動的過程中,上方消失的已經被渲染過且消失在視口的元素,也會因為消失在視口中,重新被隱藏。因此,即便頁面滾動到最下方,整體的滾動條高度還是沒有什麼變化的。

content-visibility 是否能夠優化渲染性能?

那麼,content-visibility 是否能夠優化渲染性能呢?

Youtube -- Slashing layout cost with content-visibility 中,給了一個非常好的例子。

這裡我簡單復現一下。

對於一個存在巨量 HTML 內容的頁面,譬如類似於這個頁面 -- HTML - Living Standard

可以感受到,往下翻,根本翻不到盡頭。(這裡我在本地模擬了該頁面,複製了該頁面的所有 DOM,並非實際在該網站進行測試)

如果不對這個頁面做任何處理,看看首次渲染需要花費的時間:

可以看到,DOMContentLoaded 的時間的 3s+,而花費在 Rendering 上的就有整整 2900ms

而如果給這個頁面的每個段落,添加上 content-visibility: auto,再看看整體的耗時:

可以看到,DOMContentLoaded 的時間驟降至了 500ms+,而花費在 Rendering 上的,直接優化到了 61ms

2900ms --> 61ms,可謂是驚人級別的優化了。因此,content-visibility: auto 對於長文本、長列表功能的優化是顯而易見的。

利用 contain-intrinsic-size 解決滾動條抖動問題

當然,content-visibility 也存在一些小問題。

從上面的例子,也能看到,在利用 content-visibility: auto 處理長文本、長列表的時候。在滾動頁面的過程中,滾動條一直在抖動,這不是一個很好的體驗。

當然,這也是許多虛擬列表都會存在的一些問題。

好在,規範制定者也發現了這個問題。這裡我們可以使用另外一個 CSS 屬性,也就是文章一開頭提到的另外一個屬性 -- contain-intrinsic-size,來解決這個問題。

contain-intrinsic-size:控制由 content-visibility 指定的元素的自然大小。

什麼意思呢?

還是上面的例子

<div class="g-wrap">
    <div class="paragraph">...</div>
    // ... 包含了 N 個 paragraph
    <div class="paragraph">...</div>
</div>

如果我們不使用 contain-intrinsic-size,只對視口之外的元素使用 content-visibility: auto,那麼視口外的元素高度通常就為 0。

當然,如果直接給父元素設置固定的 height,也是會有高度的。

那麼實際的滾動效果,滾動條就是抖動的:

所以,我們可以同時利用上 contain-intrinsic-size,如果能準確知道設置了 content-visibility: auto 的元素在渲染狀態下的高度,就填寫對應的高度。如果如法準確知道高度,也可以填寫一個大概的值:

.paragraph {
    content-visibility: auto;
    contain-intrinsic-size: 320px;
}

如此之後,瀏覽器會給未被實際渲染的視口之外的 .paragraph 元素一個高度,避免出現滾動條抖動的現象:

你可以自己親自嘗試感受一下:CodePen Demo -- content-visibility: auto Demo

content-visibility: auto VS LazyLoad

那麼,content-visibility: auto 是否可以替代 LazyLoad(懶載入)呢?

我們來看看我們通常對於 LazyLoad(懶載入)的一個定義。

LazyLoad:通常而言,LazyLoad 的作用在於,當頁面未滾動到相應區域,該區域內的資源(網路請求)不會被載入。反之,當頁面滾動到相應區域,相關資源的請求才會被髮起。

那麼,如果 content-visibility: auto 要能夠替代 LazyLoad,則需要做到,初始化渲染的時候,在頁面當前展示範圍外的,設定了 content-visibility: auto 的元素內的一些靜態資源不會被載入。

這裡我嘗試做了一個簡單的 DEMO:

還是藉助上述的代碼,假設我們有如下的 HTML 結構,也就是在上述代碼基礎上,插入一些圖片資源:

<div class="g-wrap">
    <div class="paragraph">...</div>
    // ... 包含了 N 個 paragraph
    <div class="paragraph">...</div>
    <div class="g-img">
      <img src="https://www.womenly.net/wp-content/uploads/2017/03/Tips-to-Maintain-the-Soft-Skin.jpg">
    </div>
    <div class="g-img">
      <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTD8kEsEE3hJ64aU-_TKQJtvKDtTOGQfT3A4A&usqp=CAU">
    </div>
    <div class="g-img">
      <img src="https://i.pinimg.com/originals/e8/ba/25/e8ba252917952f23dfc9715e942e654e.jpg">
    </div>
</div>

相應設置下 CSS:

.paragraph,
.g-img {
    content-visibility: auto;
}

當刷新頁面的時候,觀察網路請求(Network)的狀況:

即便當前頁面可視區域外的內容未被渲染,但是圖片依然會被載入!

因此,這也得到了一個非常重要的結論:

content-visibility: auto 無法直接替代 LazyLoad,設置了 content-visibility: auto 的元素在可視區外只是未被渲染,但是其中的靜態資源仍舊會在頁面初始化的時候被全部載入

所以,在實際使用中,如果你的業務中已經使用了比較完善的 Lazyload 處理長列表或者一些圖片資源,那麼 content-visibility: auto 不是更好的選擇。

可訪問性功能探究

當然,content-visibility: auto 的特性又引申出了另外一個有意思的點。

如果說可視區外的內容未被渲染,那是否會影響用戶進行全文檢索呢?畢竟這是一個非常重要的功能。

我們再來做個探究,還是上面的 DEMO,我們在首尾添加兩個特殊的字元串:

<div class="g-wrap">
    <div class="text">
        <p>content-visibility: auto 對搜索功能影響的探究</p>
    </div>
    <div class="paragraph">...</div>
    // ... 包含了 N 個 paragraph
    <div class="paragraph">...</div>
    <div class="text">
        <p>content-visibility: auto 對搜索功能影響的探究</p>
    </div>
</div>

相應設置下 CSS:

.paragraph,
.text {
    content-visibility: auto;
}

好,如此一來,在頁面刷新後,第二個 .text 是處於未被渲染狀態,我們試著全局 ctrl + F 查找一下,看看能找到幾個:

很有意思的現象,全局查找的時候,可以找到當前未被渲染的元素內的內容。

這裡,我們可以得到另外一個非常重要的點:

即便存在設置了 content-visibility: auto 的未被渲染的元素,但是它並不會影響全局的搜索功能

這也是 content-visibility 設計上充分的考慮,對可訪問性功能,或者說用戶體驗的考量,有了這一點,對於它的實際使用有著非常大的幫助。

content-visibility 的一些其他問題

首先,看看 content-visibility 的相容性(2022-06-03):

目前還是比較慘淡的,並且我沒有實際在業務中使用它,需要再等待一段時間。當然,由於該屬性屬於漸進增強一類的功能,即便​失效,也完全不影響頁面本身的展示。

同時,也有一些同學表示,利用 content-visibility: auto 只能解決部分場景,在海量 DOM 的場景下的實際效果,還有待進一步的實測。真正運用的時候,多做對比,在做取捨。

當然,現代瀏覽器已經越來越智能,類似 content-visibility 功能的屬性也越來越多,我們在性能優化的路上有了更多選擇,總歸是一件好事。

總結一下

再簡單總結一下:

  1. 在一些需要被頻繁切換顯示、隱藏狀態的元素上,使用 content-visibility: hidden,用戶代理無需重頭開始渲染它和它的子元素,能有效的提升切換時的渲染性能;
  2. content-visibility: auto 的作用更加類似於虛擬列表,使用它能極大的提升長列表、長文本頁面的渲染性能;
  3. 合理使用 contain-intrinsic-size 預估設置了content-visibility: auto 元素的高寬,可以有效的避免滾動條在滾動過程中的抖動;
  4. content-visibility: auto 無法直接替代 LazyLoad,設置了 content-visibility: auto 的元素在可視區外只是未被渲染,但是其中的靜態資源仍舊會在頁面初始化的時候被全部載入;
  5. 即便存在設置了 content-visibility: auto 的未被渲染的元素,但是它並不會影響全局的搜索功能。

最後

本文到此結束,希望對你有幫助

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、package的作用 • Oracle中包的概念與Java中包的概念非常類似,只是Java中的包是為了分類管理類,但是關鍵字都是package。 • 在一個大型項目中,可能有很多模塊,而每個模塊又有自己的過程、函數等。而這些過程、函數預設是放在一起的(如在PL/SQL中,過程預設都是放在一起的, ...
  • 導語 在使用xtrabackup8版本對mysql8版本進行備份恢復搭建從庫的時候,繼續使用xtrabackup2版本的方式,從xtrabackup_binlog_info 文件中找到gtid信息,執行purge,嘗試多次發現搭建失敗,於是對xtrabackup2和xtrbackup8版本備份流程( ...
  • 導讀: 首先簡單介紹一下網易杭州研究院情況簡介,如下圖所示: 我們公司主要從事平臺技術開發和建設方面,工作的重點方向主要在解決用戶在數據治理中的各種問題,讓用戶能更高效地管理自己的數據,進而產生更大的價值,比如如何整合現有功能流程,節省用戶使用成本;增加新平臺不斷調研,豐富平臺功能;新平臺功能、性能 ...
  • 本文介紹 SQL CASE 表達式,它是 SQL 中數一數二的重要功能,CASE 表達式的語法分為簡單 CASE 表達式和搜索 CASE 表達式兩種。 本文重點 CASE 表達式分為簡單 CASE 表達式和搜索 CASE 表達式兩種。搜索 CASE 表達式包含簡單 CASE 表達式的全部功能。 雖然 ...
  • 前言 短視頻模板,是快捷創作短視頻的一種方式,一般由專業設計師或模板創作人製作,用戶只需替換視頻模板中的部分素材,便可生成一支與模板一樣的創意視頻。這種省時省力、無需“燒腦”構思創意的“套模板”視頻創作方法,深受用戶喜愛。 應用場景 短視頻模板在短視頻APP、視頻剪輯工具、拍攝美化工具、旅游出行、電 ...
  • 前言 由於最近在使用vue3寫項目,使用vue3的前提就是要學習TypeScript,TypeScript算是JavaScript的升級版,TypeScript包含JavaScript和自己的一些特性 介紹 TypeScript是一種由微軟開發的開源、跨平臺的編程語言。它是JavaScript的超集 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 Pinia優勢 Pinia是一個全新的Vue狀態管理庫,是Vuex的代替者,尤雨溪強勢推薦 Vue2 和 Vue3 都能支持 拋棄傳統的 Mutation ,只有 state, getter 和 action ,簡化狀態管理庫 不需要嵌套 ...
  • HtmlParse 是一款基於windwos平臺的HTML文檔解析工具,可快速構建DOM樹,從而輕鬆實現網頁元素的爬取工作。DOM樹就是一個HTML文檔的節點樹,每個節點由:標簽(Tag)、屬性(Attribute)、文本(Text)三個值來描述。 所謂的HTML文檔解析,指的就是如何構建一顆DOM ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...