阿ken今天出去面试,又被面试官问到,”怎么减少页面的重排和重复“,阿ken 说了一些方法, 面试官接着问:还有其他的方法吗?阿ken有点纳,心里嘀咕,不就是这些常见的方法吗?难道还有其他神器、邪修乎?
不用多说有些jym 已经想到了 css contain,接下来我用著名的5W原则来分析contain
一、CSS Contain 是什么?
1.1 核心概念
CSS Contain 属性是 CSS Containment 规范的一部分,它允许开发者告知浏览器元素的渲染边界,从而限制浏览器在计算布局、绘制和合成时的影响范围。通过合理使用 Contain 属性,可以大幅减少页面重排与重绘的开销,提升页面性能。
1.2 解决的问题
在传统浏览器渲染中,当页面中的某个元素发生变化时,浏览器可能需要重新计算整个页面的布局和绘制,这会导致性能下降,尤其是在大型复杂页面中。CSS Contain 属性通过以下方式解决这一问题:
- 布局隔离:限制布局计算的范围
- 绘制隔离:限制绘制的范围
- 尺寸隔离:限制尺寸计算的范围
- 样式隔离:限制样式计算的范围
有点像 VUE 精确的控制到局部更新dom一样,但原理不一样,css contian 性能更高!就像一BFC 模型一样,里面的影响不到外部的,同理外部变化影响不到内部,vue 需要监测、diff 运算才能知道哪些dom需要更新,React 需要根据实情,自己写判断逻辑,是不是需要渲染更新子组件!
二、CSS Contain 的语法与取值
2.1 基本语法
contain: none | strict | content | [ size || layout || paint || style ];
2.2 取值详解
| 取值 | 描述 |
|---|---|
none | 不应用任何 containment(默认值) |
strict | 等价于 contain: size layout paint |
content | 等价于 contain: layout paint |
size | 元素的尺寸不依赖于子元素的尺寸 |
layout | 元素的布局变化不会影响其他元素,其他元素的布局变化也不会影响该元素 |
paint | 元素的绘制不会影响其他元素,其他元素的绘制也不会影响该元素 |
style | 元素的样式变化不会影响其他元素,其他元素的样式变化也不会影响该元素 |
2.3 组合使用
Contain 属性支持多个值的组合使用,例如:
/* 同时应用布局和绘制隔离 */
.contained {
contain: layout paint;
}
/* 应用尺寸、布局和绘制隔离 */
.strict-contained {
contain: size layout paint;
}
三、CSS Contain 的工作原理
3.1 浏览器渲染流程回顾
在深入理解 CSS Contain 之前,我们需要回顾浏览器的基本渲染流程(简写):
HTML → DOM Tree → CSSOM → Render Tree → Layout → Paint → Composite
- Layout(布局):计算每个元素的几何位置和大小
- Paint(绘制):将元素绘制到屏幕
- Composite(合成):将绘制的图层合并并显示到屏幕
3.2 Contain 属性如何优化渲染
CSS Contain 属性通过以下方式优化渲染流程:
-
布局隔离:当元素设置了
contain: layout时,浏览器会将该元素视为一个独立的布局容器,元素内部的布局变化不会影响外部元素,外部元素的布局变化也不会影响该元素内部。 -
绘制隔离:当元素设置了
contain: paint时,浏览器会将该元素绘制在一个独立的图层中,元素内部的绘制变化不会影响外部元素,外部元素的绘制变化也不会影响该元素内部。 -
尺寸隔离:当元素设置了
contain: size时,浏览器会认为该元素的尺寸不依赖于子元素的尺寸,因此在计算元素尺寸时不需要考虑子元素的尺寸变化。 -
样式隔离:当元素设置了
contain: style时,浏览器会限制样式计算的范围,避免样式变化影响到其他元素。
四、CSS Contain 的使用场景与最佳实践
4.1 场景一:复杂组件的性能优化
在开发复杂组件(如数据表格、下拉菜单、模态框等)时,使用 CSS Contain 属性可以将组件的渲染范围限制在组件内部,避免组件内部的变化影响整个页面的性能。
代码示例:
/* 模态框组件 */
.modal {
contain: strict; /* 等价于 size layout paint */
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
/* 数据表格组件 */
.data-table {
contain: layout paint;
overflow: auto;
}
4.2 场景二:动态内容的性能优化
当页面中存在大量动态内容(如实时更新的图表、滚动加载的列表等)时,使用 CSS Contain 属性可以限制动态内容的渲染范围,避免动态内容的更新影响整个页面的性能。
代码示例:
/* 实时更新的图表 */
.chart {
contain: layout paint;
width: 100%;
height: 300px;
}
/* 滚动加载的列表 */
.infinite-list {
contain: layout paint;
overflow: auto;
height: 500px;
}
4.3 场景三:动画性能优化
在实现复杂动画时,使用 CSS Contain 属性可以将动画元素隔离在独立的图层中,避免动画影响其他元素的渲染,从而提升动画性能。
代码示例:
/* 动画元素 */
.animated-element {
contain: strict;
animation: slide 1s infinite;
}
@keyframes slide {
0% { transform: translateX(0); }
100% { transform: translateX(100px); }
}
4.4 场景四:大型页面的性能优化
在大型复杂页面中,使用 CSS Contain 属性可以将页面划分为多个独立的渲染区域,每个区域的渲染变化不会影响其他区域,从而提升整个页面的性能。
代码示例:
/* 页面头部 */
.header {
contain: layout paint;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
}
/* 页面主体 */
.main {
contain: layout paint;
margin-top: 60px;
}
/* 页面侧边栏 */
.sidebar {
contain: layout paint;
width: 200px;
float: left;
}
/* 页面内容区域 */
.content {
contain: layout paint;
margin-left: 200px;
}
五、CSS Contain 的浏览器兼容性
5.1 支持情况
CSS Contain 属性的浏览器支持情况如下:
5.2 降级方案
对于不支持 CSS Contain 属性的浏览器,可以使用以下降级方案:
- 使用
will-change属性:提示浏览器优化元素的渲染 - 使用
transform: translateZ(0):触发 GPU 加速,创建独立图层 - 使用
overflow: hidden:限制元素的绘制范围
代码示例:
/* 降级方案 */
.contained {
contain: strict;
will-change: transform;
transform: translateZ(0);
overflow: hidden;
}
六、CSS Contain 的最佳实践
6.1 合理选择 Contain 取值
根据元素的使用场景,合理选择 Contain 属性的取值:
- 复杂组件:使用
contain: strict或contain: content - 动态内容:使用
contain: layout paint - 动画元素:使用
contain: strict - 大型页面:将页面划分为多个独立区域,每个区域使用
contain: layout paint
6.2 避免过度使用 Contain 属性
虽然 Contain 属性可以提升页面性能,但过度使用也可能导致性能下降。以下是一些需要注意的事项:
- 避免在小型元素上使用 Contain 属性:小型元素的渲染开销本身就很小,使用 Contain 属性可能不会带来明显的性能提升
- 避免在频繁变化的元素上使用 Contain 属性:频繁变化的元素可能会导致浏览器频繁创建和销毁图层,从而影响性能
- 避免在嵌套元素上过度使用 Contain 属性:嵌套元素上的 Contain 属性可能会相互影响,导致性能下降
6.3 结合其他性能优化技术
CSS Contain 属性可以与其他性能优化技术结合使用,以获得更好的性能提升:
- 使用 CSS 变量:减少重复的样式计算
- 使用 Flexbox/Grid 布局:提高布局性能
- 使用
transform和opacity:实现高性能动画 - 使用
will-change属性:提示浏览器优化元素的渲染
七、实战案例分析
7.1 案例一:电商商品列表性能优化
问题:电商商品列表通常包含大量商品卡片,当滚动列表或更新商品信息时,会导致大量的重排与重绘,影响页面性能。
优化方案:
- 使用
contain: layout paint:将每个商品卡片隔离在独立的渲染区域 - 使用
will-change属性:提示浏览器优化商品卡片的渲染 - 使用
transform: translateZ(0):触发 GPU 加速,创建独立图层
代码示例:
/* 商品卡片 */
.product-card {
contain: layout paint;
will-change: transform;
transform: translateZ(0);
width: 250px;
height: 300px;
margin: 10px;
float: left;
}
7.2 案例二:实时数据可视化性能优化
问题:实时数据可视化页面通常需要频繁更新数据,这会导致大量的重排与重绘,影响页面性能。
优化方案:
- 使用
contain: strict:将可视化组件隔离在独立的渲染区域 - 使用
requestAnimationFrame:控制数据更新的频率,避免频繁更新 - 使用
transform和opacity:实现高性能动画
代码示例:
/* 可视化组件 */
.chart {
contain: strict;
width: 100%;
height: 300px;
}
// 使用 requestAnimationFrame 控制数据更新频率
function updateData() {
// 更新数据逻辑
requestAnimationFrame(updateData);
}
requestAnimationFrame(updateData);
八、总结
CSS Contain 属性是前端性能优化的重要工具之一,它能够帮助开发者精确控制浏览器渲染范围,减少不必要的重排与重绘,从而显著提升页面性能。通过合理使用 CSS Contain 属性,开发者可以在不牺牲页面功能的前提下,大幅提升页面的性能和用户体验。
参考文献
感谢阅读!如果您有任何问题或建议,欢迎在评论区留言讨论。 如果你觉得本文对你有帮助,欢迎点赞、收藏、分享,也欢迎关注我,获取更多前端技术干货!