# 性能与优化 ## 1. 图片懒加载 ### IntersectionObserver 方案(推荐) ```js function lazyLoadImages() { const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.remove('lazy'); observer.unobserve(img); } }); }, { rootMargin: '50px' // 提前50px开始加载 }); document.querySelectorAll('img.lazy').forEach(img => observer.observe(img)); } // HTML: ``` ### 传统滚动监听方案 ```js function lazyLoadScroll() { const lazyImages = document.querySelectorAll('img.lazy'); const loadImage = throttle(() => { lazyImages.forEach(img => { if (img.getBoundingClientRect().top < window.innerHeight + 100) { img.src = img.dataset.src; img.classList.remove('lazy'); } }); }, 200); window.addEventListener('scroll', loadImage); loadImage(); // 初始加载 } ``` --- ## 2. 虚拟列表(大数据渲染优化) ```js class VirtualList { constructor({ container, itemHeight, totalItems, renderItem }) { this.container = container; this.itemHeight = itemHeight; this.totalItems = totalItems; this.renderItem = renderItem; this.visibleCount = Math.ceil(container.clientHeight / itemHeight) + 2; this.startIndex = 0; this.init(); } init() { // 创建容器结构 this.container.style.overflow = 'auto'; this.container.style.position = 'relative'; // 占位元素,撑开滚动高度 this.phantom = document.createElement('div'); this.phantom.style.height = `${this.totalItems * this.itemHeight}px`; // 实际渲染的列表容器 this.content = document.createElement('div'); this.content.style.position = 'absolute'; this.content.style.top = '0'; this.content.style.width = '100%'; this.container.appendChild(this.phantom); this.container.appendChild(this.content); this.container.addEventListener('scroll', throttle(() => this.onScroll(), 16)); this.render(); } onScroll() { const scrollTop = this.container.scrollTop; this.startIndex = Math.floor(scrollTop / this.itemHeight); this.content.style.transform = `translateY(${this.startIndex * this.itemHeight}px)`; this.render(); } render() { const fragment = document.createDocumentFragment(); const endIndex = Math.min(this.startIndex + this.visibleCount, this.totalItems); for (let i = this.startIndex; i < endIndex; i++) { const item = this.renderItem(i); item.style.height = `${this.itemHeight}px`; fragment.appendChild(item); } this.content.innerHTML = ''; this.content.appendChild(fragment); } } // 使用示例 new VirtualList({ container: document.getElementById('list'), itemHeight: 50, totalItems: 10000, renderItem: (index) => { const div = document.createElement('div'); div.textContent = `Item ${index}`; return div; } }); ``` --- ## 3. 函数节流在滚动事件中的应用 ```js // 节流函数 function throttle(fn, delay) { let lastTime = 0; let timer = null; return function(...args) { const now = Date.now(); const remaining = delay - (now - lastTime); if (remaining <= 0) { if (timer) { clearTimeout(timer); timer = null; } lastTime = now; fn.apply(this, args); } else if (!timer) { timer = setTimeout(() => { lastTime = Date.now(); timer = null; fn.apply(this, args); }, remaining); } }; } // 应用场景 window.addEventListener('scroll', throttle(() => { console.log('滚动位置:', window.scrollY); // 吸顶效果、返回顶部按钮显示等 }, 100)); window.addEventListener('resize', throttle(() => { console.log('窗口大小:', window.innerWidth, window.innerHeight); }, 200)); ``` --- ## 4. requestAnimationFrame 优化动画 ```js // 使用 rAF 实现平滑滚动 function smoothScrollTo(target, duration = 500) { const start = window.scrollY; const distance = target - start; const startTime = performance.now(); function step(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // 缓动函数 const easeProgress = 1 - Math.pow(1 - progress, 3); window.scrollTo(0, start + distance * easeProgress); if (progress < 1) { requestAnimationFrame(step); } } requestAnimationFrame(step); } // rAF 节流 function rafThrottle(fn) { let ticking = false; return function(...args) { if (!ticking) { requestAnimationFrame(() => { fn.apply(this, args); ticking = false; }); ticking = true; } }; } ``` --- ## 5. Web Worker 处理耗时任务 ```js // main.js const worker = new Worker('worker.js'); worker.postMessage({ type: 'heavyTask', data: largeArray }); worker.onmessage = (e) => { console.log('处理结果:', e.data); }; // worker.js self.onmessage = (e) => { if (e.data.type === 'heavyTask') { const result = e.data.data.map(item => /* 耗时计算 */); self.postMessage(result); } }; ``` --- ## 6. 时间切片(避免长任务阻塞) ```js async function timeSlice(tasks, chunkSize = 5) { const results = []; for (let i = 0; i < tasks.length; i += chunkSize) { const chunk = tasks.slice(i, i + chunkSize); // 处理一批任务 for (const task of chunk) { results.push(task()); } // 让出主线程 await new Promise(resolve => setTimeout(resolve, 0)); // 或使用 requestIdleCallback } return results; } // 使用 requestIdleCallback function processInIdle(tasks) { let index = 0; function work(deadline) { while (index < tasks.length && deadline.timeRemaining() > 0) { tasks[index++](); } if (index < tasks.length) { requestIdleCallback(work); } } requestIdleCallback(work); } ```