JS `Content Visibility` CSS 属性:Offscreen 内容渲染优化

各位前端的弄潮儿们,大家好!我是今天的主讲人,咱们今天聊点高性能的,保证让你的网站速度像火箭一样嗖嗖的!

今天我们要攻克的课题是 CSS 的 content-visibility 属性,一个可以拯救你卡顿页面的小秘密。别看它名字长,理解起来可简单了,用好了,能让你的网站性能直接起飞!

一、 什么是 content-visibility

简单来说,content-visibility 就像一个“内容可见性开关”。它允许浏览器跳过某些元素的渲染工作,直到这些元素真正进入视口。想象一下,你的页面有几百个组件,但用户一开始只能看到最上面的几个,那下面的组件是不是可以先“假装”不存在,等用户滚动到的时候再渲染呢?content-visibility 就是干这个的!

二、 为什么需要 content-visibility

传统的网页渲染流程是这样的:

  1. 浏览器下载 HTML、CSS、JavaScript。
  2. 浏览器解析 HTML,构建 DOM 树。
  3. 浏览器解析 CSS,构建 CSSOM 树。
  4. 浏览器将 DOM 树和 CSSOM 树合并,生成渲染树 (Render Tree)。
  5. 浏览器根据渲染树计算每个节点的位置和大小 (Layout)。
  6. 浏览器将每个节点绘制到屏幕上 (Paint)。

这个过程非常耗时,特别是对于大型页面,即使用户一开始只能看到页面顶部的一小部分内容,浏览器仍然需要渲染整个页面。这导致了两个问题:

  • 首次渲染时间 (First Render) 长: 用户需要等待很长时间才能看到页面内容。
  • 交互卡顿: 浏览器需要不断地重新渲染页面,导致页面交互卡顿。

content-visibility 的出现就是为了解决这些问题。它可以让浏览器跳过不可见内容的渲染,从而减少首次渲染时间和提高页面交互性能。

三、 content-visibility 的用法

content-visibility 属性有几个可选值:

  • visible 这是默认值,表示元素正常渲染。
  • hidden 元素被隐藏,但仍然占据空间 (类似于 visibility: hidden)。
  • auto 这是最常用的值。当元素不在视口内时,浏览器会跳过该元素的渲染,只渲染一个占位符。当元素进入视口时,浏览器才会渲染该元素。
  • contain 这个值比 auto 更进一步。它不仅跳过元素的渲染,还跳过元素的布局。这意味着浏览器不需要计算元素的大小和位置,从而进一步提高性能。

代码示例:

<!DOCTYPE html>
<html>
<head>
  <title>Content Visibility Demo</title>
  <style>
    .section {
      width: 100%;
      height: 500px;
      border: 1px solid black;
      margin-bottom: 20px;
    }

    .section.lazy {
      content-visibility: auto;
      contain-intrinsic-size: 100% 500px; /* 必须设置,否则高度会塌陷 */
    }

    .long-content {
        height: 1000px; /* 模拟大量内容 */
        background-color: lightblue;
    }
  </style>
</head>
<body>

  <div class="section">
    <h2>Section 1 (Visible)</h2>
    <p>This section is always visible.</p>
  </div>

  <div class="section lazy">
    <h2>Section 2 (Lazy Loaded)</h2>
    <div class="long-content">
        <p>大量的内容...</p>
    </div>
  </div>

  <div class="section lazy">
    <h2>Section 3 (Lazy Loaded)</h2>
     <div class="long-content">
        <p>大量的内容...</p>
    </div>
  </div>

  <div class="section lazy">
    <h2>Section 4 (Lazy Loaded)</h2>
     <div class="long-content">
        <p>大量的内容...</p>
    </div>
  </div>

  <div class="section">
    <h2>Section 5 (Visible)</h2>
    <p>This section is always visible.</p>
  </div>

</body>
</html>

解释:

  • 我们创建了几个 div 元素,并给它们添加了 section 类。
  • 对于需要懒加载的 section 元素,我们添加了 lazy 类,并将 content-visibility 设置为 auto
  • contain-intrinsic-size 这是一个非常重要的属性。当 content-visibility 设置为 autocontain 时,浏览器会跳过元素的布局。这意味着浏览器不知道元素的大小,会导致元素的高度塌陷。contain-intrinsic-size 用于告诉浏览器元素的固有大小,从而避免高度塌陷。
    • 第一个值是元素的固有宽度,第二个值是元素的固有高度。
    • 你可以使用具体的像素值,也可以使用百分比。使用百分比时,元素的高度将相对于其父元素的高度计算。
  • .long-content 模拟大量的内容,使得渲染时间更长,效果更明显。

注意事项:

  • contain-intrinsic-size 必不可少: 当你使用 content-visibility: autocontent-visibility: contain 时,一定要设置 contain-intrinsic-size,否则元素的高度会塌陷。
  • 不要滥用: content-visibility 并不是万能的。对于小型页面,过度使用 content-visibility 可能会适得其反,因为浏览器需要额外的开销来管理元素的可见性。
  • 测试: 在使用 content-visibility 之前,一定要进行测试,确保它能够提高你的网站性能。

四、 content-visibility: auto vs content-visibility: contain

这两个值都用于懒加载内容,但它们之间有一个重要的区别:

  • content-visibility: auto 浏览器跳过元素的渲染,但仍然计算元素的大小和位置。
  • content-visibility: contain 浏览器跳过元素的渲染和布局。

因此,content-visibility: containcontent-visibility: auto 更加高效,因为它减少了浏览器的计算量。但是,content-visibility: contain 也有一些限制:

  • 元素不能有副作用: 如果元素在隐藏状态下会产生副作用 (例如,修改 DOM 结构),则不能使用 content-visibility: contain
  • 元素不能依赖于其他元素的大小和位置: 如果元素的大小和位置依赖于其他元素,则不能使用 content-visibility: contain

如何选择?

一般来说,如果你的元素满足以下条件,则可以使用 content-visibility: contain

  • 元素是独立的,不依赖于其他元素。
  • 元素在隐藏状态下不会产生副作用。

否则,你应该使用 content-visibility: auto

表格总结:

特性 content-visibility: auto content-visibility: contain
渲染 跳过 跳过
布局 计算 跳过
性能 较高 最高
适用场景 大部分情况 独立、无副作用的元素
限制 较少 较多

五、 content-visibility 的兼容性

content-visibility 的兼容性还是不错的,主流浏览器都支持。

浏览器 版本 支持情况
Chrome 85+ 支持
Edge 85+ 支持
Firefox 77+ 支持 (需要手动开启 layout.css.content-visibility.enabled)
Safari 15+ 支持
iOS Safari 15+ 支持

六、 content-visibility 与 JavaScript 的结合

虽然 content-visibility 主要是一个 CSS 属性,但我们也可以使用 JavaScript 来控制元素的可见性。例如,我们可以使用 Intersection Observer API 来检测元素是否进入视口,然后动态地设置 content-visibility 属性。

代码示例:

<!DOCTYPE html>
<html>
<head>
  <title>Content Visibility with Intersection Observer</title>
  <style>
    .section {
      width: 100%;
      height: 500px;
      border: 1px solid black;
      margin-bottom: 20px;
      content-visibility: hidden; /* 初始隐藏 */
      contain-intrinsic-size: 100% 500px;
    }

    .section.visible {
      content-visibility: auto; /* 进入视口后自动渲染 */
    }

    .long-content {
        height: 1000px;
        background-color: lightblue;
    }
  </style>
</head>
<body>

  <div class="section" id="section1">
    <h2>Section 1 (Lazy Loaded)</h2>
    <div class="long-content">
        <p>大量的内容...</p>
    </div>
  </div>

  <div class="section" id="section2">
    <h2>Section 2 (Lazy Loaded)</h2>
     <div class="long-content">
        <p>大量的内容...</p>
    </div>
  </div>

  <div class="section" id="section3">
    <h2>Section 3 (Lazy Loaded)</h2>
     <div class="long-content">
        <p>大量的内容...</p>
    </div>
  </div>

  <script>
    const sections = document.querySelectorAll('.section');

    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          entry.target.classList.add('visible');
          observer.unobserve(entry.target); // 停止观察,避免重复触发
        }
      });
    });

    sections.forEach(section => {
      observer.observe(section);
    });
  </script>

</body>
</html>

解释:

  1. 初始隐藏: 我们首先将所有 section 元素的 content-visibility 设置为 hidden,这意味着它们一开始是隐藏的。
  2. Intersection Observer: 我们使用 Intersection Observer API 来检测元素是否进入视口。
  3. 进入视口: 当元素进入视口时,我们给它添加一个 visible 类。这个类会将 content-visibility 设置为 auto,从而触发元素的渲染。
  4. 停止观察: 一旦元素进入视口,我们就停止观察它,避免重复触发渲染。

七、 content-visibility 的实际应用场景

content-visibility 在以下场景中特别有用:

  • 长列表: 如果你的页面包含一个很长的列表,可以使用 content-visibility 来懒加载列表项。
  • 大型图片库: 如果你的页面包含一个大型图片库,可以使用 content-visibility 来懒加载图片。
  • 包含复杂组件的页面: 如果你的页面包含很多复杂的组件,可以使用 content-visibility 来懒加载这些组件。
  • 单页应用 (SPA): 在 SPA 中,页面通常包含很多隐藏的组件,可以使用 content-visibility 来提高页面的初始加载速度。

八、 总结

content-visibility 是一个强大的 CSS 属性,可以显著提高网站的性能。通过跳过不可见内容的渲染,它可以减少首次渲染时间和提高页面交互性能。但是,content-visibility 并不是万能的,需要根据实际情况选择合适的值,并进行充分的测试。

记住,性能优化是一个持续的过程,需要不断地学习和实践。希望今天的分享能够帮助你更好地理解和使用 content-visibility,让你的网站性能更上一层楼!

课后作业:

  1. 尝试在你的项目中应用 content-visibility,看看能否提高网站性能。
  2. 研究一下 content-visibility 的其他用法,例如与 CSS Grid 和 Flexbox 结合使用。
  3. 分享你使用 content-visibility 的经验和心得。

好了,今天的讲座就到这里,希望大家有所收获!下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注