270 lines
6.0 KiB
Markdown
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);
|
|
}
|
|
```
|