2026-01-19 09:34:13 +08:00

270 lines
6.0 KiB
Markdown

# 性能与优化
## 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: <img class="lazy" data-src="real-image.jpg" src="placeholder.jpg" />
```
### 传统滚动监听方案
```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);
}
```