Resources
3Install
npx skillscat add pluginagentmarketplace/custom-plugin-javascript/performance Install via the SkillsCat registry.
About this skill
This skill outlines JavaScript techniques for optimizing execution speed and improving Core Web Vitals metrics such as LCP, INP, and CLS. It addresses performance bottlenecks that cause slow page loads and unresponsive interactions. Use it when developing or auditing web pages to meet speed targets and enhance user experience.
SKILL.md
JavaScript Performance Skill
Quick Reference Card
Core Web Vitals
| Metric | Target | Measures |
|---|---|---|
| LCP | < 2.5s | Largest content paint |
| INP | < 200ms | Interaction responsiveness |
| CLS | < 0.1 | Visual stability |
Debounce & Throttle
// Debounce - delay until pause
function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
// Throttle - limit frequency
function throttle(fn, interval) {
let lastCall = 0;
return (...args) => {
const now = Date.now();
if (now - lastCall >= interval) {
lastCall = now;
fn(...args);
}
};
}
// Usage
window.addEventListener('scroll', throttle(handleScroll, 100));
input.addEventListener('input', debounce(search, 300));DOM Performance
// Batch DOM reads/writes
const heights = elements.map(el => el.offsetHeight); // Read all
elements.forEach((el, i) => el.style.height = heights[i] + 'px'); // Write all
// Use DocumentFragment
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
fragment.appendChild(li);
});
list.appendChild(fragment);
// Use requestAnimationFrame
function animate() {
// Update DOM
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);Code Splitting
// Dynamic import
const module = await import('./heavy-module.js');
// React lazy
const HeavyComponent = React.lazy(() => import('./Heavy'));
<Suspense fallback={<Spinner />}>
<HeavyComponent />
</Suspense>Memory Optimization
// WeakMap for metadata (auto cleanup)
const metadata = new WeakMap();
metadata.set(element, { clicks: 0 });
// Remove event listeners
const controller = new AbortController();
el.addEventListener('click', handler, { signal: controller.signal });
controller.abort(); // Cleanup
// Clear references
let heavyData = loadData();
// When done:
heavyData = null;Web Workers
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => console.log('Result:', e.data);
// worker.js
self.onmessage = (e) => {
const result = processData(e.data);
self.postMessage(result);
};Profiling
Performance API
// Mark and measure
performance.mark('start');
doWork();
performance.mark('end');
performance.measure('work', 'start', 'end');
const [measure] = performance.getEntriesByName('work');
console.log('Duration:', measure.duration);
// Resource timing
const resources = performance.getEntriesByType('resource');
resources.forEach(r => {
console.log(r.name, r.duration);
});Console Timing
console.time('operation');
await doSomething();
console.timeEnd('operation');Troubleshooting
Common Issues
| Problem | Symptom | Fix |
|---|---|---|
| Jank | Stuttering scroll | Use throttle, optimize DOM |
| Memory leak | Growing memory | Clear refs, remove listeners |
| Long task | UI freeze | Split work, use workers |
| Large bundle | Slow load | Code split, tree shake |
Debug Checklist
// 1. Profile in DevTools Performance panel
// 2. Check for long tasks (> 50ms)
// 3. Analyze bundle with webpack-bundle-analyzer
// 4. Run Lighthouse audit
// 5. Monitor with web-vitals libraryProduction Patterns
Lazy Loading Images
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});Virtual Scrolling
// Only render visible items
function renderVisibleItems(scrollTop, containerHeight) {
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = startIndex + Math.ceil(containerHeight / itemHeight);
return items.slice(startIndex, endIndex);
}Related
- Agent 08: Testing & Quality (detailed learning)
- Skill: debugging: Performance debugging
- Skill: ecosystem: Build optimization