减少重绘与回流的技术
理解重绘与回流
浏览器渲染页面时,会经历解析HTML构建DOM树、解析CSS构建CSSOM树、合并成渲染树、布局计算、绘制像素等步骤。当DOM元素的外观发生变化但不影响布局时(如修改颜色),触发重绘;当布局属性改变(如宽度、位置),则触发回流,导致重新计算所有受影响元素的几何属性。回流必然引起重绘,但重绘不一定触发回流。
// 触发回流的操作
element.style.width = '200px';
// 仅触发重绘的操作
element.style.color = 'red';
避免频繁操作样式
连续修改样式会导致浏览器多次重排。最佳实践是将多个样式变更合并为一次操作:
// 错误示范 - 多次触发回流
el.style.margin = '5px';
el.style.padding = '10px';
el.style.width = '100px';
// 正确做法 - 使用cssText或class
el.style.cssText = 'margin:5px; padding:10px; width:100px';
// 或
el.classList.add('active-style');
使用文档片段优化DOM操作
直接操作DOM会触发即时回流。DocumentFragment可以作为内存中的DOM节点容器:
const fragment = document.createDocumentFragment();
for(let i=0; i<100; i++){
const item = document.createElement('div');
item.textContent = `Item ${i}`;
fragment.appendChild(item);
}
document.body.appendChild(fragment); // 仅一次回流
批量读取布局属性
强制同步布局(Layout Thrashing)发生在交替读写布局属性时:
// 低效写法 - 导致多次回流
for(let i=0; i<boxes.length; i++) {
boxes[i].style.width = boxes[i].offsetWidth + 10 + 'px';
}
// 优化方案 - 先读后写
const widths = boxes.map(box => box.offsetWidth);
boxes.forEach((box, i) => {
box.style.width = widths[i] + 10 + 'px';
});
使用transform和opacity实现动画
CSS3属性不会触发布局计算:
.animate {
transform: translateX(100px); /* 使用GPU加速 */
opacity: 0.5; /* 复合层优化 */
will-change: transform; /* 提前告知浏览器 */
}
优化样式表结构
选择器匹配从右向左进行,避免深层嵌套:
/* 低效选择器 */
div ul li a span.highlight { ... }
/* 优化选择器 */
.highlight { ... }
使用虚拟列表处理长列表
只渲染可视区域内的元素:
function renderVisibleItems(container, items, scrollTop) {
const itemHeight = 50;
const startIdx = Math.floor(scrollTop / itemHeight);
const endIdx = startIdx + Math.ceil(container.clientHeight / itemHeight);
container.innerHTML = '';
for(let i=startIdx; i<=endIdx; i++) {
const item = document.createElement('div');
item.textContent = items[i] || '';
container.appendChild(item);
}
}
分离读写操作
利用浏览器的渲染队列机制:
// 触发一次回流
element.style.display = 'none'; // 写
// 中间可以插入其他操作
element.style.width = '100px'; // 写
element.style.display = 'block'; // 写
使用Flexbox/Grid布局
现代布局方式比传统浮动/定位更高效:
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
合理管理事件监听器
高频事件需要进行节流/防抖:
function debounce(fn, delay) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, arguments), delay);
};
}
window.addEventListener('resize', debounce(handleResize, 100));
使用content-visibility属性
跳过屏幕外元素的渲染:
.long-list-item {
content-visibility: auto;
contain-intrinsic-size: 100px 500px;
}
优化图片加载
使用正确的尺寸和格式:
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" loading="lazy" decoding="async">
</picture>
减少CSS表达式
避免使用动态计算的CSS值:
/* 避免使用 */
width: expression(document.body.clientWidth > 800 ? "800px" : "auto");
使用requestAnimationFrame
将视觉变化集中在浏览器重绘周期:
function animate() {
element.style.transform = `translateX(${position}px)`;
position += 1;
if(position < 100) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
离线DOM操作
处理复杂DOM时先使其脱离文档流:
const container = document.getElementById('container');
container.style.display = 'none'; // 触发一次回流
// 执行大量DOM操作
container.style.display = 'block'; // 触发一次回流
避免表格布局
表格会延迟渲染直到所有单元格就绪:
<!-- 避免 -->
<table>
<tr><td>内容</td></tr>
</table>
<!-- 推荐 -->
<div class="grid-container">
<div class="grid-item">内容</div>
</div>
使用CSS Containment
限制浏览器重绘/回流范围:
.widget {
contain: layout paint style;
}
优化字体加载
避免布局偏移和不可见文本闪烁:
@font-face {
font-family: 'MyFont';
src: url('myfont.woff2') format('woff2');
font-display: swap;
}
减少阴影和渐变使用
复杂视觉效果会增加重绘成本:
/* 优化前 */
box-shadow: 0 0 10px rgba(0,0,0,0.5), inset 0 0 5px #fff;
/* 优化后 */
box-shadow: 0 0 5px rgba(0,0,0,0.3);
使用Web Workers处理计算
将耗时任务移出主线程:
// main.js
const worker = new Worker('compute.js');
worker.postMessage(data);
worker.onmessage = (e) => updateUI(e.data);
// compute.js
self.onmessage = (e) => {
const result = heavyComputation(e.data);
self.postMessage(result);
};
避免强制同步布局
某些API会强制立即计算布局:
// 触发强制布局
const width = element.offsetWidth; // 读取
element.style.width = width + 10 + 'px'; // 写入
使用CSS变量减少样式变动
通过变量批量更新样式:
:root {
--theme-color: #4285f4;
}
.button {
background: var(--theme-color);
}
// JavaScript只需修改一处
document.documentElement.style.setProperty('--theme-color', '#ea4335');
优化第三方脚本加载
使用async/defer属性:
<script src="analytics.js" defer></script>
<script src="widget.js" async></script>
使用Intersection Observer
替代滚动事件监听:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
});
document.querySelectorAll('.lazy-load').forEach(el => {
observer.observe(el);
});
避免内联样式
外部样式表更利于浏览器优化:
<!-- 避免 -->
<div style="width:100px; color:red;"></div>
<!-- 推荐 -->
<style>
.box { width:100px; color:red; }
</style>
<div class="box"></div>
使用CSS will-change
提前告知浏览器可能的变化:
.animated-element {
will-change: transform, opacity;
}
优化SVG使用
减少SVG复杂度并正确使用:
<svg width="100" height="100">
<rect x="10" y="10" width="80" height="80" fill="#4285f4"/>
</svg>
减少图层数量
过多的复合层会增加内存消耗:
/* 避免不必要的硬件加速 */
.element {
transform: translateZ(0); /* 谨慎使用 */
}
使用CSS Content属性
减少额外DOM节点:
.icon-check:before {
content: "✓";
color: green;
}
优化滚动性能
启用硬件加速的滚动:
.container {
overflow-y: scroll;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
使用媒体查询按需加载
根据设备特性加载资源:
<link rel="stylesheet" href="mobile.css" media="(max-width: 600px)">
避免@import规则
阻塞并行下载:
/* 避免 */
@import url('styles.css');
/* 推荐 */
<link rel="stylesheet" href="styles.css">
使用CSS计数器
替代JavaScript计数:
ol {
counter-reset: section;
}
li::before {
counter-increment: section;
content: counters(section, ".") " ";
}
优化表单元素
减少布局复杂的表单控件:
<input type="range" class="custom-slider">
使用CSS aspect-ratio
避免布局跳动:
.video-container {
aspect-ratio: 16/9;
}
减少伪元素滥用
复杂的伪元素会增加绘制成本:
/* 适度使用 */
.button::after {
content: "";
position: absolute;
/* 简单效果 */
}
使用CSS revert
重置为浏览器默认样式:
.reset-styles {
all: revert;
}
优化Web字体使用
选择适当的字体格式和子集:
@font-face {
font-family: 'Open Sans';
src: url('OpenSans.woff2') format('woff2');
unicode-range: U+000-5FF; /* 拉丁字符子集 */
}
使用CSS gap属性
替代margin实现间距:
.grid {
display: grid;
gap: 20px;
}
避免频繁的className切换
合并样式变更:
// 低效
element.classList.add('active');
element.classList.remove('inactive');
// 优化
element.className = 'element active';
使用CSS Scroll Snap
实现精准滚动定位:
.container {
scroll-snap-type: y mandatory;
}
.slide {
scroll-snap-align: start;
}
优化Canvas绘制
减少canvas状态变更:
// 批量设置状态
ctx.fillStyle = 'red';
ctx.fillRect(10,10,50,50);
ctx.fillRect(70,10,50,50);
// 使用路径批量绘制
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(50,50);
ctx.lineTo(10,50);
ctx.fill();
使用CSS prefers-reduced-motion
尊重用户运动偏好:
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
优化阴影效果
使用适当的阴影参数:
/* 优化前 */
box-shadow: 0 0 20px 10px rgba(0,0,0,0.5);
/* 优化后 */
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
使用CSS contain-intrinsic-size
为content-visibility提供占位尺寸:
.lazy-item {
content-visibility: auto;
contain-intrinsic-size: 300px 200px;
}
避免不必要的z-index
复杂的层叠上下文会增加计算成本:
/* 避免过度使用 */
.modal {
z-index: 9999;
}
使用CSS line-clamp
实现多行文本截断:
.ellipsis {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
优化背景图渲染
避免复杂背景模式:
/* 优化前 */
background: linear-gradient(45deg, #000 25%, transparent 25%) -50px 0,
linear-gradient(-45deg, #000 25%, transparent 25%) -50px 0;
/* 优化后 */
background: #f5f5f5 url('simple-pattern.png');
使用CSS overscroll-behavior
控制滚动链行为:
.modal-content {
overscroll-behavior-y: contain;
}
优化CSS变量使用
避免频繁更新CSS变量:
// 低效
element.style.setProperty('--x', x + 'px');
element.style.setProperty('--y', y + 'px');
// 优化
element.style.cssText = `--x:${x}px; --y:${y}px`;
使用CSS revert-layer
回滚到上一层级的样式:
@layer base {
a { color: blue; }
}
@layer theme {
a { color: red; }
.special-link {
color: revert-layer; /* 回退到base层的蓝色 */
}
}
优化渐变性能
简化渐变定义:
/* 优化前 */
background: linear-gradient(to right,
#ff0000, #ff4000, #ff8000, #ffbf00, #ffff00);
/* 优化后 */
background: linear-gradient(to right, #ff0000, #ffff00);
使用CSS accent-color
快速设置表单控件主题色:
input[type="checkbox"] {
accent-color: #4285f4;
}
优化滤镜效果
避免复杂滤镜链:
/* 优化前 */
filter: blur(5px) brightness(1.2) contrast(0.8) saturate(1.5);
/* 优化后 */
filter: brightness(1.1);
使用CSS :where()选择器
降低选择器特异性:
/* 高特异性 */
article > .title { color: blue; }
/* 低特异性 */
:where(article > .title) { color: blue; }
优化CSS自定义属性
合理组织变量:
:root {
--spacing-sm: 8px;
--spacing-md: 16px;
--color-primary: #4285f4;
}
.card {
padding: var(--spacing-md);
border: 1px solid var(--color-primary);
}
使用CSS :has()选择器
减少JavaScript依赖:
/* 当包含图片时添加边框 */
.card:has(img) {
border: 1px solid #eee;
}
优化CSS变量计算
避免复杂的calc()运算:
/* 优化前 */
--size: calc(var(--base-size) * 1.5 + 10px);
/* 优化后 */
--size: 25px; /* 预计算值 */
使用CSS :is()选择器
简化选择器组:
/* 简化前 */
.header > h1,
.header > h2,
.header > h3 {
margin: 0;
}
/* 简化后 */
.header > :is(h1, h2, h3) {
margin: 0;
}
优化CSS媒体查询顺序
按移动优先原则组织:
/* 基础样式 - 移动设备 */
.container { padding: 10px; }
/* 中等屏幕 */
@media (min-width: 768px) {
.container { padding: 20px; }
}
/* 大屏幕 */
@media (min-width: 1024px) {
.container { padding: 30px; }
}
使用CSS :focus-visible
改善键盘导航体验:
button:focus-visible {
outline: 2px solid blue;
}
优化CSS网格布局
明确轨道定义提高性能:
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: minmax(100px, auto);
}
使用CSS :empty伪类
处理空内容状态:
.message:empty::before {
content: "暂无消息";
color: gray;
}
优化CSS过渡动画
指定特定属性进行过渡:
/* 优化前 */
transition: all 0.3s ease;
/* 优化后 */
transition: opacity 0.3s ease, transform 0.3s ease;
使用CSS @supports规则
渐进增强样式:
@supports (display: grid) {
.container {
display: grid;
}
}
@supports not (display: grid) {
.container {
display: flex;
}
}
优化CSS计数器使用
减少计数器更新频率:
/* 整个列表使用一个计数器 */
ol {
counter-reset: item;
}
li {
counter-increment: item;
}
li::before {
content: counter(item);
}
使用CSS :target伪类
实现无JS交互效果:
.tab-content {
display: none;
}
.tab-content:target {
display: block;
}
优化CSS混合模式
谨慎使用blend-mode:
/* 仅在需要时使用 */
.overlay {
mix-blend-mode: multiply;
}
使用CSS :invalid伪类
增强表单验证UI:
input:invalid {
border-color: red;
}
优化CSS视口单位
结合固定值使用:
.header {
height: calc(100vh - 60px);
}
使用CSS :nth-child()选择器
替代JavaScript循环:
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn