Techniques to reduce repaints and reflows
Understanding Repaint and Reflow
When a browser renders a page, it goes through steps such as parsing HTML to build the DOM tree, parsing CSS to build the CSSOM tree, merging them into a render tree, layout calculations, and pixel painting. When the appearance of a DOM element changes without affecting the layout (e.g., modifying the color), a repaint is triggered. When layout properties change (e.g., width, position), a reflow is triggered, causing the browser to recalculate the geometric properties of all affected elements. Reflow inevitably triggers a repaint, but repaint does not necessarily trigger reflow.
// Operations that trigger reflow
element.style.width = '200px';
// Operations that only trigger repaint
element.style.color = 'red';
Avoid Frequent Style Manipulations
Consecutive style modifications can cause the browser to reflow multiple times. The best practice is to combine multiple style changes into a single operation:
// Bad example - triggers multiple reflows
el.style.margin = '5px';
el.style.padding = '10px';
el.style.width = '100px';
// Correct approach - use cssText or class
el.style.cssText = 'margin:5px; padding:10px; width:100px';
// Or
el.classList.add('active-style');
Optimize DOM Operations with Document Fragments
Direct DOM manipulation triggers immediate reflow. DocumentFragment can serve as an in-memory container for DOM nodes:
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); // Only one reflow
Batch Read Layout Properties
Forced synchronous layout (Layout Thrashing) occurs when reading and writing layout properties alternately:
// Inefficient approach - causes multiple reflows
for(let i=0; i<boxes.length; i++) {
boxes[i].style.width = boxes[i].offsetWidth + 10 + 'px';
}
// Optimized approach - read first, then write
const widths = boxes.map(box => box.offsetWidth);
boxes.forEach((box, i) => {
box.style.width = widths[i] + 10 + 'px';
});
Use Transform and Opacity for Animations
CSS3 properties do not trigger layout calculations:
.animate {
transform: translateX(100px); /* Uses GPU acceleration */
opacity: 0.5; /* Optimizes composite layers */
will-change: transform; /* Informs the browser in advance */
}
Optimize Stylesheet Structure
Selector matching occurs from right to left; avoid deep nesting:
/* Inefficient selector */
div ul li a span.highlight { ... }
/* Optimized selector */
.highlight { ... }
Use Virtual Lists for Long Lists
Only render elements within the visible area:
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);
}
}
Separate Read and Write Operations
Leverage the browser's rendering queue mechanism:
// Triggers one reflow
element.style.display = 'none'; // Write
// Other operations can be inserted here
element.style.width = '100px'; // Write
element.style.display = 'block'; // Write
Use Flexbox/Grid Layouts
Modern layout methods are more efficient than traditional floats/positioning:
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
Manage Event Listeners Properly
High-frequency events require throttling/debouncing:
function debounce(fn, delay) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, arguments), delay);
};
}
window.addEventListener('resize', debounce(handleResize, 100));
Use the content-visibility Property
Skip rendering off-screen elements:
.long-list-item {
content-visibility: auto;
contain-intrinsic-size: 100px 500px;
}
Optimize Image Loading
Use the correct size and format:
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" loading="lazy" decoding="async">
</picture>
Avoid CSS Expressions
Avoid dynamically calculated CSS values:
/* Avoid using */
width: expression(document.body.clientWidth > 800 ? "800px" : "auto");
Use requestAnimationFrame
Concentrate visual changes during the browser's repaint cycle:
function animate() {
element.style.transform = `translateX(${position}px)`;
position += 1;
if(position < 100) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Offline DOM Operations
For complex DOM manipulations, first remove them from the document flow:
const container = document.getElementById('container');
container.style.display = 'none'; // Triggers one reflow
// Perform extensive DOM operations
container.style.display = 'block'; // Triggers one reflow
Avoid Table Layouts
Tables delay rendering until all cells are ready:
<!-- Avoid -->
<table>
<tr><td>Content</td></tr>
</table>
<!-- Recommended -->
<div class="grid-container">
<div class="grid-item">Content</div>
</div>
Use CSS Containment
Limit the scope of repaints/reflows:
.widget {
contain: layout paint style;
}
Optimize Font Loading
Avoid layout shifts and invisible text flashes:
@font-face {
font-family: 'MyFont';
src: url('myfont.woff2') format('woff2');
font-display: swap;
}
Reduce Shadow and Gradient Usage
Complex visual effects increase repaint costs:
/* Before optimization */
box-shadow: 0 0 10px rgba(0,0,0,0.5), inset 0 0 5px #fff;
/* After optimization */
box-shadow: 0 0 5px rgba(0,0,0,0.3);
Use Web Workers for Computations
Move time-consuming tasks off the main thread:
// 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);
};
Avoid Forced Synchronous Layout
Certain APIs force immediate layout calculations:
// Triggers forced layout
const width = element.offsetWidth; // Read
element.style.width = width + 10 + 'px'; // Write
Use CSS Variables to Reduce Style Changes
Batch update styles via variables:
:root {
--theme-color: #4285f4;
}
.button {
background: var(--theme-color);
}
// JavaScript only needs to modify one place
document.documentElement.style.setProperty('--theme-color', '#ea4335');
Optimize Third-Party Script Loading
Use async/defer attributes:
<script src="analytics.js" defer></script>
<script src="widget.js" async></script>
Use Intersection Observer
Replace scroll event listeners:
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);
});
Avoid Inline Styles
External stylesheets are more conducive to browser optimization:
<!-- Avoid -->
<div style="width:100px; color:red;"></div>
<!-- Recommended -->
<style>
.box { width:100px; color:red; }
</style>
<div class="box"></div>
Use CSS will-change
Inform the browser of potential changes in advance:
.animated-element {
will-change: transform, opacity;
}
Optimize SVG Usage
Reduce SVG complexity and use correctly:
<svg width="100" height="100">
<rect x="10" y="10" width="80" height="80" fill="#4285f4"/>
</svg>
Reduce Layer Count
Excessive composite layers increase memory consumption:
/* Avoid unnecessary hardware acceleration */
.element {
transform: translateZ(0); /* Use with caution */
}
Use CSS Content Property
Reduce additional DOM nodes:
.icon-check:before {
content: "✓";
color: green;
}
Optimize Scroll Performance
Enable hardware-accelerated scrolling:
.container {
overflow-y: scroll;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
Use Media Queries for On-Demand Loading
Load resources based on device features:
<link rel="stylesheet" href="mobile.css" media="(max-width: 600px)">
Avoid @import Rules
Block parallel downloads:
/* Avoid */
@import url('styles.css');
/* Recommended */
<link rel="stylesheet" href="styles.css">
Use CSS Counters
Replace JavaScript counters:
ol {
counter-reset: section;
}
li::before {
counter-increment: section;
content: counters(section, ".") " ";
}
Optimize Form Elements
Reduce layout complexity for form controls:
<input type="range" class="custom-slider">
Use CSS aspect-ratio
Avoid layout jumps:
.video-container {
aspect-ratio: 16/9;
}
Reduce Pseudo-Element Abuse
Complex pseudo-elements increase painting costs:
/* Use in moderation */
.button::after {
content: "";
position: absolute;
/* Simple effects */
}
Use CSS revert
Reset to browser default styles:
.reset-styles {
all: revert;
}
Optimize Web Font Usage
Choose appropriate font formats and subsets:
@font-face {
font-family: 'Open Sans';
src: url('OpenSans.woff2') format('woff2');
unicode-range: U+000-5FF; /* Latin character subset */
}
Use CSS gap Property
Replace margins for spacing:
.grid {
display: grid;
gap: 20px;
}
Avoid Frequent className Toggling
Combine style changes:
// Inefficient
element.classList.add('active');
element.classList.remove('inactive');
// Optimized
element.className = 'element active';
Use CSS Scroll Snap
Achieve precise scroll positioning:
.container {
scroll-snap-type: y mandatory;
}
.slide {
scroll-snap-align: start;
}
Optimize Canvas Drawing
Reduce canvas state changes:
// Batch set states
ctx.fillStyle = 'red';
ctx.fillRect(10,10,50,50);
ctx.fillRect(70,10,50,50);
// Use paths for batch drawing
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(50,50);
ctx.lineTo(10,50);
ctx.fill();
Use CSS prefers-reduced-motion
Respect user motion preferences:
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Optimize Shadow Effects
Use appropriate shadow parameters:
/* Before optimization */
box-shadow: 0 0 20px 10px rgba(0,0,0,0.5);
/* After optimization */
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
Use CSS contain-intrinsic-size
Provide placeholder sizes for content-visibility:
.lazy-item {
content-visibility: auto;
contain-intrinsic-size: 300px 200px;
}
Avoid Unnecessary z-index
Complex stacking contexts increase calculation costs:
/* Avoid overuse */
.modal {
z-index: 9999;
}
Use CSS line-clamp
Implement multi-line text truncation:
.ellipsis {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
Optimize Background Image Rendering
Avoid complex background patterns:
/* Before optimization */
background: linear-gradient(45deg, #000 25%, transparent 25%) -50px 0,
linear-gradient(-45deg, #000 25%, transparent 25%) -50px 0;
/* After optimization */
background: #f5f5f5 url('simple-pattern.png');
Use CSS overscroll-behavior
Control scroll chaining behavior:
.modal-content {
overscroll-behavior-y: contain;
}
Optimize CSS Variable Usage
Avoid frequent updates to CSS variables:
// Inefficient
element.style.setProperty('--x', x + 'px');
element.style.setProperty('--y', y + 'px');
// Optimized
element.style.cssText = `--x:${x}px; --y:${y}px`;
Use CSS revert-layer
Revert to styles from the previous layer:
@layer base {
a { color: blue; }
}
@layer theme {
a { color: red; }
.special-link {
color: revert-layer; /* Falls back to blue from base layer */
}
}
Optimize Gradient Performance
Simplify gradient definitions:
/* Before optimization */
background: linear-gradient(to right,
#ff0000, #ff4000, #ff8000, #ffbf00, #ffff00);
/* After optimization */
background: linear-gradient(to right, #ff0000, #ffff00);
Use CSS accent-color
Quickly set form control theme colors:
input[type="checkbox"] {
accent-color: #4285f4;
}
Optimize Filter Effects
Avoid complex filter chains:
/* Before optimization */
filter: blur(5px) brightness(1.2) contrast(0.8) saturate(1.5);
/* After optimization */
filter: brightness(1.1);
Use CSS :where() Selector
Reduce selector specificity:
/* High specificity */
article > .title { color: blue; }
/* Low specificity */
:where(article > .title) { color: blue; }
Optimize CSS Custom Properties
Organize variables logically:
:root {
--spacing-sm: 8px;
--spacing-md: 16px;
--color-primary: #4285f4;
}
.card {
padding: var(--spacing-md);
border: 1px solid var(--color-primary);
}
Use CSS :has() Selector
Reduce JavaScript dependency:
/* Add border when containing an image */
.card:has(img) {
border: 1px solid #eee;
}
Optimize CSS Variable Calculations
Avoid complex calc() operations:
/* Before optimization */
--size: calc(var(--base-size) * 1.5 + 10px);
/* After optimization */
--size: 25px; /* Precomputed value */
Use CSS :is() Selector
Simplify selector groups:
/* Before simplification */
.header > h1,
.header > h2,
.header > h3 {
margin: 0;
}
/* After simplification */
.header > :is(h1, h2, h3) {
margin: 0;
}
Optimize CSS Media Query Order
Organize following mobile-first principles:
/* Base styles - mobile devices */
.container { padding: 10px; }
/* Medium screens */
@media (min-width: 768px) {
.container { padding: 20px; }
}
/* Large screens */
@media (min-width: 1024px) {
.container { padding: 30px; }
}
Use CSS :focus-visible
Improve keyboard navigation experience:
button:focus-visible {
outline: 2px solid blue;
}
Optimize CSS Grid Layout
Define tracks explicitly for better performance:
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-rows: minmax(100px, auto);
}
Use CSS :empty Pseudo-Class
Handle empty content states:
.message:empty::before {
content: "No messages";
color: gray;
}
Optimize CSS Transitions and Animations
Specify specific properties for transitions:
/* Before optimization */
transition: all 0.3s ease;
/* After optimization */
transition: opacity 0.3s ease, transform 0.3s ease;
Use CSS @supports Rule
Progressive enhancement for styles:
@supports (display: grid) {
.container {
display: grid;
}
}
@supports not (display: grid) {
.container {
display: flex;
}
}
Optimize CSS Counter Usage
Reduce counter update frequency:
/* Use one counter for the entire list */
ol {
counter-reset: item;
}
li {
counter-increment: item;
}
li::before {
content: counter(item);
}
Use CSS :target Pseudo-Class
Implement JS-free interactive effects:
.tab-content {
display: none;
}
.tab-content:target {
display: block;
}
Optimize CSS Blend Modes
Use blend-mode cautiously:
/* Only use when necessary */
.overlay {
mix-blend-mode: multiply;
}
Use CSS :invalid Pseudo-Class
Enhance form validation UI:
input:invalid {
border-color: red;
}
Optimize CSS Viewport Units
Combine with fixed values:
.header {
height: calc
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn