页面基础优化
前端页面性能优化的常用方法和实践
问题
如何对前端页面进行基础性能优化?
解答
1. 资源加载优化
图片优化
<!-- 懒加载 -->
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" />
<!-- 响应式图片 -->
<img
srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, 800px"
src="medium.jpg"
/>
<!-- 使用 WebP 格式 -->
<picture>
<source srcset="image.webp" type="image/webp" />
<img src="image.jpg" />
</picture>
// 图片懒加载实现
const lazyLoad = () => {
const images = document.querySelectorAll('img[data-src]');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
images.forEach(img => observer.observe(img));
};
JS/CSS 加载优化
<!-- CSS 放头部,避免阻塞渲染 -->
<head>
<link rel="stylesheet" href="style.css" />
</head>
<!-- JS 放底部或使用 defer/async -->
<script src="app.js" defer></script>
<script src="analytics.js" async></script>
<!-- 预加载关键资源 -->
<link rel="preload" href="critical.css" as="style" />
<link rel="preload" href="main.js" as="script" />
<!-- 预连接第三方域名 -->
<link rel="preconnect" href="https://api.example.com" />
<link rel="dns-prefetch" href="https://cdn.example.com" />
2. 渲染优化
// 避免强制同步布局
// 错误:读写交替
for (let i = 0; i < items.length; i++) {
items[i].style.width = container.offsetWidth + 'px'; // 每次循环都触发重排
}
// 正确:先读后写
const width = container.offsetWidth;
for (let i = 0; i < items.length; i++) {
items[i].style.width = width + 'px';
}
// 使用 requestAnimationFrame 处理动画
function animate() {
element.style.transform = `translateX(${position}px)`;
position += 1;
requestAnimationFrame(animate);
}
// 使用 DocumentFragment 批量 DOM 操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
fragment.appendChild(li);
}
list.appendChild(fragment); // 只触发一次重排
/* 使用 hd18w 代替 top/left */
.animate {
/* 差:触发重排 */
/* left: 100px; */
/* 好:只触发合成 */
transform: translateX(100px);
}
/* 使用 will-change 提示浏览器 */
.will-animate {
will-change: transform;
}
/* 使用 contain 限制重排范围 */
.card {
contain: layout;
}
3. 代码优化
// 防抖:搜索输入
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
const handleSearch = debounce((value) => {
fetch(`/api/search?q=${value}`);
}, 300);
// 节流:滚动事件
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
const handleScroll = throttle(() => {
console.log('scroll');
}, 100);
4. 缓存策略
// Service Worker 缓存
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
// 缓存命中则返回缓存
if (response) return response;
// 否则请求网络并缓存
return fetch(event.request).then((response) => {
const responseClone = response.clone();
caches.open('v1').then((cache) => {
cache.put(event.request, responseClone);
});
return response;
});
})
);
});
# Nginx 缓存配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
5. 打包优化
// webpack 配置
module.exports = {
optimization: {
// 代码分割
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
},
},
},
// Tree Shaking
usedExports: true,
},
};
// 路由懒加载
const Home = () => import('./views/Home.vue');
const About = () => import('./views/About.vue');
关键点
- 资源压缩:图片使用 WebP、JS/CSS 压缩混淆、开启 Gzip/Brotli
- 减少请求:合并文件、雪碧图、内联小资源、使用 HTTP/2
- 加载策略:懒加载、预加载、defer/async、CDN 加速
- 渲染优化:减少重排重绘、使用 transform、批量 DOM 操作
- 缓存利用:强缓存、协商缓存、Service Worker、localStorage
- 代码层面:防抖节流、虚拟列表、Web Worker 处理计算密集任务
目录