2026-01-19 09:43:07 +08:00

288 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 性能与优化
## 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. 虚拟列表(大数据渲染优化)
> **场景**:聊天记录、日志列表、表格万级数据、任何长列表滚动场景。
> **解决**:渲染万级 DOM 节点卡顿,只渲染可视区域的元素,大幅提升性能。
```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 优化动画
> **场景**平滑滚动、CSS 动画替代方案、Canvas/WebGL 渲染循环、游戏开发。
> **解决**setTimeout/setInterval 动画帧率不稳定rAF 与屏幕刷新率同步,动画更流畅。
```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 处理耗时任务
> **场景**大文件解析Excel/CSV、图像处理、复杂计算加密/压缩)、数据分析。
> **解决**耗时JS计算阻塞主线程导致页面卡顿Worker 在后台线程执行不影响UI。
```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. 时间切片(避免长任务阻塞)
> **场景**React Fiber 架构原理、大量 DOM 初始化、批量数据处理不卡顿UI。
> **解决**长任务占据主线程超过50ms导致用户交互无响应分片执行保证响应性。
```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);
}
```