Reduce DOM manipulation and repaints
Understanding the Cost of DOM Manipulation and Repainting
DOM manipulation is one of the most common tasks in front-end development, but frequent DOM operations can lead to performance issues. The browser's rendering engine needs to recalculate layouts (reflow) and repaint, both of which are resource-intensive processes. Every time you modify the style or structure of a DOM element, the browser must recalculate the geometric properties of the affected elements and update the render tree.
// Inefficient DOM operation example
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
document.body.appendChild(div);
}
Batching DOM Modifications
The most effective way to reduce DOM operations is to batch modifications. The browser provides DocumentFragment as a lightweight DOM container, allowing you to build DOM structures in memory and then insert them into the document all at once.
// Optimizing with DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
fragment.appendChild(div);
}
document.body.appendChild(fragment);
Another method is to hide elements (set display:none
), make multiple modifications, and then show the elements again. This avoids triggering repaints during the modification process.
Using CSS Classes Instead of Style Modifications
Directly modifying an element's style
property triggers an immediate repaint. A better approach is to predefine CSS classes and then toggle class names to change styles.
/* CSS definition */
.highlight {
background-color: yellow;
font-weight: bold;
}
// Modifying styles via class names
element.classList.add('highlight');
For complex animations, use CSS transform
and opacity
properties, as they enable high-performance animations without triggering reflows.
Virtual DOM and Differential Updates
Modern front-end frameworks like React and Vue use Virtual DOM technology. They first construct a Virtual DOM tree in memory, compare it with the actual DOM to identify differences, and then update only the necessary parts.
// React example
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
}
Even without a framework, you can implement similar optimization strategies: calculate all changes first, then apply them to the DOM in one go.
Optimizing Event Handling
Frequently triggered events (e.g., scroll
, resize
, mousemove
) can easily cause performance issues. Use debounce or throttle techniques to reduce the frequency of event handling.
// Throttle implementation
function throttle(func, limit) {
let inThrottle;
return function() {
if (!inThrottle) {
func.apply(this, arguments);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
window.addEventListener('resize', throttle(handleResize, 100));
Using requestAnimationFrame for Animation Optimization
For JavaScript animations, use requestAnimationFrame
instead of setTimeout
or setInterval
. It automatically synchronizes with the browser's refresh rate, avoiding unnecessary repaints.
function animate() {
// Animation logic
element.style.transform = `translateX(${position}px)`;
if (position < 500) {
position += 5;
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Reducing Layout Thrashing
Layout thrashing occurs when you continuously read and write DOM layout properties, forcing the browser to recalculate layouts multiple times. Avoid this by batching property reads and modifications.
// Bad practice - causes layout thrashing
for (let i = 0; i < items.length; i++) {
items[i].style.width = items[i].offsetWidth + 10 + 'px';
}
// Optimized approach
const widths = items.map(item => item.offsetWidth);
for (let i = 0; i < items.length; i++) {
items[i].style.width = widths[i] + 10 + 'px';
}
Using Modern CSS Layout Techniques
Flexbox and Grid layouts are more efficient than traditional float layouts, as they reduce unnecessary nesting levels and complex CSS calculations.
/* Using Flexbox instead of float layouts */
.container {
display: flex;
justify-content: space-between;
}
.item {
flex: 1;
}
Leveraging Browser Developer Tools
Modern browser developer tools offer performance analysis features to identify code that causes excessive repaints and reflows.
- Use the Performance panel to record page activity.
- Check Layout and Paint events.
- Use the Paint flashing feature in the Rendering panel to visualize repaint areas.
Caching DOM Query Results
Repeatedly querying DOM elements is a common performance bottleneck. Store DOM query results in variables for later use.
// Bad practice
for (let i = 0; i < 100; i++) {
document.querySelector('.item').style.color = 'red';
}
// Optimized approach
const item = document.querySelector('.item');
for (let i = 0; i < 100; i++) {
item.style.color = 'red';
}
Using CSS Containment
The CSS contain
property tells the browser that a certain element's subtree is independent of the rest of the document, allowing the browser to optimize rendering.
.widget {
contain: layout paint style;
}
This property is particularly useful for frequently updated independent components, such as chat messages or notifications.
Avoiding Table Layouts
Table layouts force the browser to wait for the entire table to load before rendering, causing performance issues. For non-tabular data, use other layout methods.
<!-- Avoid unnecessary table layouts -->
<div class="grid">
<div class="row">
<div class="cell">Content 1</div>
<div class="cell">Content 2</div>
</div>
</div>
Optimizing Images and Media Resources
Unoptimized images can cause layout changes and repaints. You should:
- Use the correct image dimensions.
- Implement lazy loading.
- Consider modern formats like WebP.
- Set fixed aspect ratios for images.
<img src="image.jpg" loading="lazy" width="800" height="600" alt="Example image">
Using Web Workers for Complex Calculations
Moving time-consuming JavaScript calculations to Web Workers avoids blocking the main thread and reduces interface lag.
// Main thread
const worker = new Worker('worker.js');
worker.postMessage(data);
worker.onmessage = function(e) {
// Process results
};
// worker.js
self.onmessage = function(e) {
const result = heavyComputation(e.data);
self.postMessage(result);
};
Proper Use of will-change
The will-change
property hints to the browser which properties might change, allowing it to prepare in advance.
.element {
will-change: transform, opacity;
}
However, avoid overusing it—only apply it to elements that will indeed change frequently.
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:懒加载与按需加载