要让前端页面更快、响应更稳、体验更丝滑,需要从 代码优化
、资源加载
、渲染性能
、网络请求
、缓存策略
等多个方面入手。以下是具体优化方案。
代码优化
- 代码拆分(Code Splitting)
目标:
按需加载代码
,减少首屏 JS 体积
。适用场景:
React、Vue、Angular 等 SPA(单页应用)
。实现流程(以 React 为例)
使用
React.lazy
动态导入组件// const LazyComponent = React.lazy(() => import('./LazyComponent')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> ); } // React.lazy+ Suspense实现组件懒加载 // fallback是加载时的占位组件(如 Loading 动画)。
Webpack 自动代码分割
// webpack.config.js module.exports = { optimization: { splitChunks: { chunks: 'all', // 自动拆分公共依赖(如 lodash) }, }, };
- Tree Shaking(摇树优化)
目标:删除未使用的代码,减少打包体积。
适用场景:ES6 模块化项目(使用 import/export)。
实现流程
确保使用 ES6 模块语法
// 正确(支持 Tree Shaking) import { debounce } from 'lodash-es'; // 错误(无法 Tree Shaking) import _ from 'lodash';
Webpack 配置(生产模式默认启用)
// webpack.config.js module.exports = { mode: 'production', // 自动启用 Tree Shaking };```
检查优化效果
使用 webpack-bundle-analyzer分析打包结果:npm install --save-dev webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = { plugins: [new BundleAnalyzerPlugin()], };
- 避免长任务(Long Tasks)
- 目标:防止 JavaScript 阻塞主线程,导致页面卡顿。
- 实现流程
拆分耗时任务
// 使用 setTimeout 分片执行 function processChunk(data, chunkSize, callback) { let index = 0; function next() { const chunk = data.slice(index, index + chunkSize); if (chunk.length > 0) { callback(chunk); index += chunkSize; setTimeout(next, 0); // 让出主线程 } } next(); }
使用 Web Workers 处理 CPU 密集型任务
// worker.js self.onmessage = (e) => { const result = heavyCalculation(e.data); self.postMessage(result); }; // 主线程 const worker = new Worker('worker.js'); worker.postMessage(data); worker.onmessage = (e) => console.log(e.data);
资源加载优化
1. 图片优化
- 目标:减少图片体积,提升加载速度。
- 实现流程
使用 WebP或 AVIF格式
<picture> <source srcset="image.avif" type="image/avif"> <source srcset="image.webp" type="image/webp"> <img src="image.jpg" alt="Fallback"> </picture>
懒加载非首屏图片
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" />
// 动态加载(兼容旧浏览器) document.addEventListener('DOMContentLoaded', () => { const lazyImages = document.querySelectorAll('img[data-src]'); lazyImages.forEach(img => { img.src = img.dataset.src; }); });
使用 srcset适配不同分辨率
<img srcset="small.jpg 480w, medium.jpg 768w, large.jpg 1200w" sizes="(max-width: 600px) 480px, 800px" src="fallback.jpg" />
- 字体优化
- 目标:避免 FOIT(字体未加载时的空白期)。
- 实现流程
使用 font-display: swap
@font-face { font-family: 'Open Sans'; src: url('font.woff2') format('woff2'); font-display: swap; /* 先显示备用字体,再替换 */ }
预加载关键字体
<link rel="preload" href="font.woff2" as="font" crossorigin>
- 预加载关键资源
目标:提前加载首屏必需的 JS/CSS/字体。
实现流程
使用
<link rel="preload" href="main.js" as="script"> <link rel="preload" href="styles.css" as="style">
HTTP/2 Server Push(需服务器支持)
# Nginx 配置 location / { http2_push /style.css; http2_push /app.js; }
渲染性能优化
- 减少重排(Reflow)和重绘(Repaint)
目标:避免频繁触发浏览器重新计算布局。
实现流程
使用 transform和 opacity做动画
.box { transform: translateX(100px); /* 不会触发重排 */ opacity: 0.5; /* 只触发重绘 */ }
避免强制同步布局(Layout Thrashing
// 错误:读写交替导致多次重排 for (let i = 0; i < items.length; i++) { items[i].style.width = '100px'; console.log(items[i].offsetWidth); } // 正确:先读后写 const widths = items.map(item => item.offsetWidth); items.forEach(item => item.style.width = '100px');
- 虚拟列表(Virtual List)
- 目标:优化长列表渲染性能,仅渲染可见项。
- 实现流程(React)
使用 react-window
npm install react-window import { FixedSizeList as List } from 'react-window'; const Row = ({ index, style }) => ( <div style={style}>Row {index}</div> ); function App() { return ( <List height={400} itemCount={1000} itemSize={50} width={300}> {Row} </List> ); }
网络请求优化
- 缓存策略
- 目标:减少重复请求,提升二次访问速度。
- 实现流程
强缓存(Cache-Control)
Nginx 配置(1年缓存) location /static { expires 1y; add_header Cache-Control "public, immutable"; }
Service Worker 离线缓存
// sw.js self.addEventListener('install', (e) => { e.waitUntil( caches.open('v1').then(cache => { return cache.addAll(['/', '/styles.css', '/app.js']); }) ); });
监控与持续优化
1. Lighthouse 性能分析
目标:量化性能指标并优化。
实现流程
Chrome DevTools 运行 Lighthouse
- 打开 Chrome DevTools → Lighthouse → 生成报告。
- 关注 FCP、LCP、CLS 等核心指标。
使用 web-vitals库监控真实用户数据
npm install web-vitals import { getCLS, getFID, getLCP } from 'web-vitals'; getCLS(console.log); getFID(console.log); getLCP(console.log);