DOM performance optimization
The Impact of DOM Operations on Performance
DOM operations are typically one of the most performance-intensive parts of JavaScript. Each DOM update triggers the browser's reflow and repaint processes, both of which are highly resource-consuming. Frequent DOM operations can cause page lag and degrade user experience.
// Inefficient DOM operation example
for (let i = 0; i < 1000; i++) {
document.getElementById('container').innerHTML += `<div>Item ${i}</div>`;
}
Minimizing DOM Access
Each DOM access causes the browser to recalculate layouts, so direct DOM access should be minimized. DOM references should be cached for reuse.
// Optimized code
const container = document.getElementById('container');
let html = '';
for (let i = 0; i < 1000; i++) {
html += `<div>Item ${i}</div>`;
}
container.innerHTML = html;
Using DocumentFragment
DocumentFragment is a lightweight document object that can temporarily store DOM nodes before inserting them into the real DOM all at once.
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = `Item ${i}`;
fragment.appendChild(div);
}
document.getElementById('container').appendChild(fragment);
Batch Style Modifications
Directly modifying an element's style property triggers repaints. Instead, modify styles in bulk by changing classes.
// Not recommended
element.style.width = '100px';
element.style.height = '200px';
element.style.backgroundColor = 'red';
// Recommended
element.classList.add('active-style');
Event Delegation
Binding event handlers to numerous child elements consumes significant memory. Event delegation should be used instead.
// Traditional way - binding events to each button
const buttons = document.querySelectorAll('.btn');
buttons.forEach(btn => {
btn.addEventListener('click', handleClick);
});
// Event delegation - binding a single event to the parent
document.getElementById('button-container').addEventListener('click', function(e) {
if (e.target.classList.contains('btn')) {
handleClick(e);
}
});
Using requestAnimationFrame
For animations or frequent visual updates, use requestAnimationFrame instead of setTimeout or setInterval.
function animate() {
// Animation logic
element.style.left = `${pos}px`;
if (pos < 100) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Virtual DOM Technology
Modern frontend frameworks like React and Vue use virtual DOM technology to minimize actual DOM operations by comparing changes in the virtual DOM tree.
// React example
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
}
Optimizing Selector Performance
Complex selectors increase the time browsers take to match elements. Simple selectors should be preferred.
// Not recommended
document.querySelector('div.container > ul.list > li.item');
// Recommended
document.getElementById('list-item');
Avoiding Forced Synchronous Layouts
Reading certain properties (like offsetWidth) forces the browser to perform synchronous layout calculations. Avoid frequently reading these properties in loops.
// Forced synchronous layout example - poor performance
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = elements[i].offsetWidth + 10 + 'px';
}
// Optimized code
const widths = [];
for (let i = 0; i < elements.length; i++) {
widths[i] = elements[i].offsetWidth;
}
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = widths[i] + 10 + 'px';
}
Using CSS Animations Instead of JavaScript Animations
CSS animations generally perform better than JavaScript animations as they can leverage hardware acceleration.
/* CSS animation */
.box {
transition: transform 0.3s ease;
}
.box.move {
transform: translateX(100px);
}
Lazy Loading and Virtual Lists
For long lists or large numbers of DOM elements, lazy loading or virtual list techniques should be employed.
// Virtual list implementation example
class VirtualList {
constructor(container, itemHeight, renderItem) {
this.container = container;
this.itemHeight = itemHeight;
this.renderItem = renderItem;
this.visibleItems = Math.ceil(container.clientHeight / itemHeight);
this.data = [];
container.addEventListener('scroll', this.handleScroll.bind(this));
}
setData(data) {
this.data = data;
this.render();
}
handleScroll() {
this.render();
}
render() {
const scrollTop = this.container.scrollTop;
const startIdx = Math.floor(scrollTop / this.itemHeight);
const endIdx = startIdx + this.visibleItems;
this.container.innerHTML = '';
for (let i = startIdx; i <= endIdx && i < this.data.length; i++) {
const item = document.createElement('div');
item.style.height = `${this.itemHeight}px`;
item.style.position = 'absolute';
item.style.top = `${i * this.itemHeight}px`;
item.style.width = '100%';
this.renderItem(item, this.data[i]);
this.container.appendChild(item);
}
this.container.style.height = `${this.data.length * this.itemHeight}px`;
}
}
Using MutationObserver Instead of DOM Events
For monitoring DOM changes, MutationObserver is more efficient than traditional DOM events.
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
// Handle DOM changes
});
});
observer.observe(document.getElementById('target'), {
attributes: true,
childList: true,
subtree: true
});
Optimizing Image Loading
While image elements aren't DOM operations, they impact overall performance. Image loading strategies should be optimized.
// Lazy loading images
document.addEventListener('DOMContentLoaded', () => {
const lazyImages = [].slice.call(document.querySelectorAll('img.lazy'));
if ('IntersectionObserver' in window) {
const lazyImageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(lazyImage => {
lazyImageObserver.observe(lazyImage);
});
}
});
Using Web Workers for Complex Calculations
Offload complex calculations to Web Workers to avoid blocking the main thread and DOM updates.
// Main thread code
const worker = new Worker('worker.js');
worker.postMessage({ data: largeDataSet });
worker.onmessage = function(e) {
document.getElementById('result').textContent = e.data.result;
};
// worker.js
self.onmessage = function(e) {
const result = processData(e.data.data);
self.postMessage({ result });
};
function processData(data) {
// Complex calculations
return processedResult;
}
Proper Use of Debouncing and Throttling
For frequently triggered events (like scroll, resize), use debouncing or throttling techniques.
// Throttle implementation
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function() {
const context = this;
const args = arguments;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function() {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
}
}
window.addEventListener('scroll', throttle(function() {
// Handle scroll event
}, 200));
Using CSS Containment
The CSS Containment property tells browsers that an element's subtree is independent of the rest of the page, optimizing rendering performance.
.container {
contain: layout paint style;
}
Avoiding Table Layouts
Table layouts force browsers to wait for the entire table to load before rendering. Table layouts should be avoided.
<!-- Not recommended -->
<table>
<tr>
<td>Content 1</td>
<td>Content 2</td>
</tr>
</table>
<!-- Recommended -->
<div class="grid-container">
<div class="grid-item">Content 1</div>
<div class="grid-item">Content 2</div>
</div>
Using will-change Property
For elements that will change, use the will-change property to hint browsers to optimize in advance.
.element {
will-change: transform, opacity;
}
Optimizing SVG Performance
SVG elements are part of the DOM. Large numbers of SVGs impact performance, so SVG usage should be optimized.
// Reuse SVG elements instead of creating new instances
const svgNS = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(svgNS, 'svg');
const use = document.createElementNS(svgNS, 'use');
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#icon');
svg.appendChild(use);
document.body.appendChild(svg);
Using CSS Variables to Reduce Style Calculations
CSS variables can reduce style calculation time, especially when styles need frequent modification.
:root {
--main-color: #06c;
}
.element {
color: var(--main-color);
}
Avoiding @import
@import in CSS blocks parallel loading. Use <link> tags instead.
<!-- Not recommended -->
<style>
@import url('styles.css');
</style>
<!-- Recommended -->
<link rel="stylesheet" href="styles.css">
Using content-visibility Property
The content-visibility property can skip rendering off-screen content, significantly improving initial load performance.
.container {
content-visibility: auto;
contain-intrinsic-size: 500px;
}
Optimizing Font Loading
Font files impact rendering performance. Font loading strategies should be optimized.
@font-face {
font-family: 'MyFont';
src: url('myfont.woff2') format('woff2');
font-display: swap;
}
Using Intersection Observer for Lazy Loading
The Intersection Observer API efficiently detects when elements enter the viewport.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('img.lazy').forEach(img => {
observer.observe(img);
});
Reducing DOM Depth
Excessively deep DOM trees increase style calculation and layout time. DOM nesting should be minimized.
<!-- Not recommended -->
<div>
<div>
<div>
<div>
<p>Content</p>
</div>
</div>
</div>
</div>
<!-- Recommended -->
<div>
<p>Content</p>
</div>
Using CSS Grid and Flexbox Layouts
Modern layout techniques (CSS Grid and Flexbox) generally perform better than traditional layouts.
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
Avoiding Frequent Reading of Layout Properties
Frequently reading layout properties like offsetWidth and offsetHeight in JavaScript triggers forced synchronous layouts.
// Not recommended
function resizeElements(elements) {
elements.forEach(element => {
element.style.width = (element.offsetWidth + 10) + 'px';
});
}
// Recommended
function resizeElements(elements) {
const widths = elements.map(element => element.offsetWidth);
elements.forEach((element, index) => {
element.style.width = (widths[index] + 10) + 'px';
});
}
Using CSS Containment to Optimize Complex Components
For complex components, use CSS Containment to isolate their rendering impact.
.widget {
contain: layout paint style;
}
Optimizing Third-Party Script Loading
Third-party scripts often impact page performance. Their loading should be optimized.
<!-- Deferred loading -->
<script src="analytics.js" defer></script>
<!-- Asynchronous loading -->
<script src="analytics.js" async></script>
<!-- Dynamic loading -->
<script>
window.addEventListener('load', function() {
const script = document.createElement('script');
script.src = 'analytics.js';
document.body.appendChild(script);
});
</script>
Using Service Worker to Cache Resources
Service Workers can cache resources needed for DOM operations, improving subsequent visit speeds.
// service-worker.js
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache => {
return cache.addAll([
'/',
'/styles.css',
'/script.js',
'/data.json'
]);
})
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
Using Passive Event Listeners
For events that won't call preventDefault(), use the passive option to improve scroll performance.
document.addEventListener('scroll', function(e) {
// Handle scroll event
}, { passive: true });
Optimizing Canvas Performance
While Canvas operations aren't DOM-related, they impact overall performance. Canvas usage should be optimized.
// Optimizing Canvas drawing
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Offscreen Canvas
const offscreen = document.createElement('canvas');
offscreen.width = 100;
offscreen.height = 100;
const offscreenCtx = offscreen.getContext('2d');
// Draw complex graphics on offscreen Canvas
offscreenCtx.beginPath();
// ...Complex drawing operations
offscreenCtx.fill();
// Draw offscreen content on main Canvas
ctx.drawImage(offscreen, 0, 0);
Using ResizeObserver Instead of resize Event
ResizeObserver is more efficient than traditional resize events for monitoring element size changes.
const observer = new ResizeObserver(entries => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
// Handle size changes
}
});
observer.observe(document.getElementById('resizable-element'));
Optimizing Web Font Usage
Web fonts impact text rendering performance. Font loading and usage should be optimized.
@font-face {
font-family: 'OptimizedFont';
src: url('font.woff2') format('woff2');
font-display: swap;
font-weight: 400;
font-style: normal;
unicode-range: U+000-5FF; /* Only load needed character ranges */
}
Using CSS Content-Visibility to Optimize Long Lists
For long lists, the content-visibility property can significantly improve rendering performance.
.long-list-item {
content-visibility: auto;
contain-intrinsic-size: 100px 50px; /* Estimated size */
}
Optimizing Shadow and Blur Effects
CSS shadows and filter effects consume performance and should be used judiciously.
/* Not recommended */
.element {
box-shadow: 0 0 20px 10px rgba(0,0,0,0.5);
filter: blur(5px);
}
/* Recommended - more efficient shadows */
.element {
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
Using CSS Hardware Acceleration
For animated elements, enable hardware acceleration to improve performance.
.animate {
transform: translateZ(0);
will-change: transform;
}
Optimizing Media Queries
Complex media queries increase style calculation time. Media query logic should be optimized.
/* Not recommended - overly complex media queries */
@media screen and (min-width: 600px) and (max-width: 800px) and (orientation: landscape) and (-webkit-min-device-pixel-ratio: 2) {
/* Styles */
}
/* Recommended - simplified media queries */
@media (min-width: 600px) {
/* Styles */
}
Using CSS Scroll Snap
CSS Scroll Snap provides smoother scrolling experiences and reduces JavaScript scroll handling.
.container {
scroll-snap-type: y mandatory;
overflow-y: scroll;
height: 100vh;
}
.section {
scroll-snap-align: start;
height: 100vh;
}
Optimizing CSS Selectors
Complex selectors increase style calculation time. Selectors should be simplified.
/* Not recommended */
div.container > ul.list > li.item > a.link:hover {
color: red;
}
/* Recommended */
.link:hover {
color: red;
}
Using CSS Variables for Theme Switching
CSS variables can efficiently implement theme switching, avoiding massive style repaints.
:root {
--primary-color: #0066cc;
--background: white;
}
.dark-theme {
--primary-color: #33a6ff;
--background: #121212;
}
body {
background: var(--background);
color: var(--primary-color);
}
Optimizing Canvas Drawing Performance
For Canvas animations, optimize drawing logic to reduce repaint areas.
// Only repaint changed areas
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Only draw what needs updating
updateParticles();
drawParticles();
requestAnimationFrame(animate);
}
Using CSS Containment to Isolate Animations
For complex animations, use CSS Containment to isolate their impact scope.
.animated-element {
contain: strict;
will-change: transform;
}
Optimizing Web Component Performance
Custom web components require performance optimization, especially for lifecycle methods.
class OptimizedElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
// Defer non-critical operations
requestIdleCallback(() => {
this.loadLazyContent();
});
}
loadLazyContent() {
// Load deferred content
}
}
Using CSS Overscroll Behavior to Control Scroll Propagation
Prevent scroll events from propagating to parent elements.
.container {
overscroll-behavior: contain;
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:事件处理机制
下一篇:window对象核心API