前端老旧项目全面性能优化指南与面试攻略
前端老旧项目全面性能优化指南与面试攻略
目录
- 项目性能分析与诊断
- 代码层面优化
- 资源加载优化
- 运行时性能优化
- 网络请求优化
- 构建与部署优化
- 监控与持续优化
- 面试回答技巧
- 实战案例分析
项目性能分析与诊断
1. 性能指标了解
核心 Web Vitals
-
LCP (Largest Contentful Paint): 最大内容绘制时间
- 良好: ≤ 2.5s
- 需要改进: 2.5s - 4s
- 差: > 4s
-
FID (First Input Delay): 首次输入延迟
- 良好: ≤ 100ms
- 需要改进: 100ms - 300ms
- 差: > 300ms
-
CLS (Cumulative Layout Shift): 累积布局偏移
- 良好: ≤ 0.1
- 需要改进: 0.1 - 0.25
- 差: > 0.25
其他重要指标
- FCP (First Contentful Paint): 首次内容绘制
- TTI (Time to Interactive): 可交互时间
- TBT (Total Blocking Time): 总阻塞时间
- SI (Speed Index): 速度指数
2. 性能分析工具使用
浏览器开发者工具
// Performance API 分析
const observer = new PerformanceObserver((list) => {list.getEntries().forEach((entry) => {console.log('Performance entry:', entry);});
});observer.observe({entryTypes: ['navigation', 'resource', 'paint', 'largest-contentful-paint']
});// 手动标记关键时间点
performance.mark('component-start');
// ... 组件渲染逻辑
performance.mark('component-end');
performance.measure('component-render', 'component-start', 'component-end');
Lighthouse 自动化分析
// lighthouse-ci.js 配置
module.exports = {ci: {collect: {url: ['http://localhost:3000'],numberOfRuns: 3,},assert: {assertions: {'categories:performance': ['warn', { minScore: 0.9 }],'categories:accessibility': ['error', { minScore: 0.9 }],},},upload: {target: 'lhci',serverBaseUrl: 'https://your-lhci-server.com',},},
};
WebPageTest 分析
- 多地区测试
- 真实设备测试
- 网络条件模拟
- 详细的瀑布图分析
3. 性能问题诊断清单
加载性能问题
- 资源文件过大(JS、CSS、图片)
- 关键资源未优先加载
- 阻塞渲染的资源
- 网络请求过多
- 缓存策略不合理
运行时性能问题
- JavaScript 执行时间过长
- 频繁的 DOM 操作
- 内存泄漏
- 未优化的事件监听器
- 重复的计算或渲染
代码层面优化
1. JavaScript 优化
代码分割与懒加载
// 路由级别的代码分割
const routes = [{path: '/home',component: () => import('./views/Home.vue')},{path: '/profile',component: () => import('./views/Profile.vue')}
];// 组件级别的懒加载
const LazyComponent = React.lazy(() => import('./LazyComponent'));function App() {return (<Suspense fallback={<div>Loading...</div>}><LazyComponent /></Suspense>);
}
避免阻塞主线程
// 使用 requestIdleCallback 进行任务调度
function performHeavyTask(data) {const chunks = chunkArray(data, 100);function processChunk(index = 0) {if (index >= chunks.length) return;requestIdleCallback((deadline) => {while (deadline.timeRemaining() > 0 && index < chunks.length) {processDataChunk(chunks[index]);index++;}if (index < chunks.length) {processChunk(index);}});}processChunk();
}// Web Workers 处理CPU密集型任务
// main.js
const worker = new Worker('calculation-worker.js');
worker.postMessage({ data: largeDataSet });
worker.onmessage = (e) => {console.log('计算结果:', e.data);
};// calculation-worker.js
self.onmessage = function(e) {const result = heavyCalculation(e.data);self.postMessage(result);
};
内存优化
// 防止内存泄漏
class ComponentManager {constructor() {this.timers = new Set();this.observers = new Set();this.eventListeners = new Map();}addTimer(id) {this.timers.add(id);}addObserver(observer) {this.observers.add(observer);}addEventListener(element, event, handler) {element.addEventListener(event, handler);if (!this.eventListeners.has(element)) {this.eventListeners.set(element, []);}this.eventListeners.get(element).push({ event, handler });}cleanup() {// 清理定时器this.timers.forEach(id => clearInterval(id));this.timers.clear();// 清理观察者this.observers.forEach(observer => observer.disconnect());this.observers.clear();// 清理事件监听器this.eventListeners.forEach((events, element) => {events.forEach(({ event, handler }) => {element.removeEventListener(event, handler);});});this.eventListeners.clear();}
}
2. CSS 优化
关键 CSS 提取
// 使用 critical 包提取关键CSS
const critical = require('critical');critical.generate({inline: true,base: 'dist/',src: 'index.html',target: {css: 'critical.css',html: 'index-critical.html'},width: 1300,height: 900,minify: true
});
CSS 优化技巧
/* 避免深层嵌套和复杂选择器 */
/* 不好的做法 */
.header .nav .menu li a:hover {color: blue;
}/* 好的做法 */
.nav-link:hover {color: blue;
}/* 使用 CSS containment 优化渲染性能 */
.card {contain: layout style paint;
}/* 使用 will-change 提示浏览器优化 */
.animated-element {will-change: transform;
}.animated-element.done {will-change: auto; /* 动画完成后移除 */
}/* 使用 CSS 变量减少重复计算 */
:root {--primary-color: #007bff;--border-radius: 4px;--shadow: 0 2px 4px rgba(0,0,0,0.1);
}
CSS 加载优化
<!-- 预加载关键字体 -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin><!-- 异步加载非关键CSS -->
<link rel="preload" href="/css/non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/non-critical.css"></noscript>
资源加载优化
1. 图片优化
现代图片格式
<!-- 使用 picture 元素支持多种格式 -->
<picture><source srcset="image.avif" type="image/avif"><source srcset="image.webp" type="image/webp"><img src="image.jpg" alt="描述" loading="lazy">
</picture>
响应式图片
<!-- 基于视口宽度的响应式图片 -->
<img src="small.jpg"srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"sizes="(max-width: 480px) 100vw, (max-width: 800px) 50vw, 33vw"alt="响应式图片">
图片懒加载实现
// 使用 Intersection Observer 实现懒加载
class LazyImageLoader {constructor() {this.imageObserver = new IntersectionObserver(this.onImageIntersection.bind(this),{ rootMargin: '50px' });this.init();}init() {const lazyImages = document.querySelectorAll('img[data-src]');lazyImages.forEach(img => this.imageObserver.observe(img));}onImageIntersection(entries) {entries.forEach(entry => {if (entry.isIntersecting) {this.loadImage(entry.target);this.imageObserver.unobserve(entry.target);}});}loadImage(img) {img.src = img.dataset.src;img.onload = () => img.classList.add('loaded');img.onerror = () => img.classList.add('error');}
}new LazyImageLoader();
2. 字体优化
字体加载策略
/* 字体显示策略 */
@font-face {font-family: 'CustomFont';src: url('/fonts/custom.woff2') format('woff2');font-display: swap; /* 立即显示后备字体 */
}/* 字体预加载 */
<link rel="preload" href="/fonts/critical.woff2" as="font" type="font/woff2" crossorigin>
字体子集化
// 使用 fonttools 进行字体子集化
const fontSubset = require('font-subset');fontSubset({font: 'source-font.ttf',text: '需要的文字内容',output: 'subset-font.woff2'
});
3. 资源预加载策略
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//api.example.com"><!-- 预连接 -->
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin><!-- 资源预加载 -->
<link rel="preload" href="/critical.css" as="style">
<link rel="preload" href="/hero-image.jpg" as="image"><!-- 预获取下一页资源 -->
<link rel="prefetch" href="/next-page.html"><!-- 模块预加载 -->
<link rel="modulepreload" href="/modules/app.js">
运行时性能优化
1. DOM 操作优化
批量 DOM 操作
// 使用 DocumentFragment 减少重排重绘
function batchUpdateDOM(items) {const fragment = document.createDocumentFragment();items.forEach(item => {const element = document.createElement('div');element.textContent = item.text;element.className = item.className;fragment.appendChild(element);});// 一次性插入所有元素document.getElementById('container').appendChild(fragment);
}// 使用 requestAnimationFrame 优化动画
function animateElement(element, from, to, duration) {const start = performance.now();function update(currentTime) {const elapsed = currentTime - start;const progress = Math.min(elapsed / duration, 1);const current = from + (to - from) * progress;element.style.transform = `translateX(${current}px)`;if (progress < 1) {requestAnimationFrame(update);}}requestAnimationFrame(update);
}
虚拟滚动实现
class VirtualScroller {constructor(container, itemHeight, items) {this.container = container;this.itemHeight = itemHeight;this.items = items;this.visibleCount = Math.ceil(container.clientHeight / itemHeight) + 2;this.startIndex = 0;this.init();}init() {this.container.style.height = `${this.items.length * this.itemHeight}px`;this.container.addEventListener('scroll', this.onScroll.bind(this));this.render();}onScroll() {const scrollTop = this.container.scrollTop;const newStartIndex = Math.floor(scrollTop / this.itemHeight);if (newStartIndex !== this.startIndex) {this.startIndex = newStartIndex;this.render();}}render() {const endIndex = Math.min(this.startIndex + this.visibleCount,this.items.length);const visibleItems = this.items.slice(this.startIndex, endIndex);this.container.innerHTML = visibleItems.map((item, index) => `<div class="item" style="position: absolute;top: ${(this.startIndex + index) * this.itemHeight}px;height: ${this.itemHeight}px;">${item.content}</div>`).join('');}
}
2. 事件优化
事件委托
// 使用事件委托减少事件监听器数量
class EventDelegator {constructor(container) {this.container = container;this.handlers = new Map();this.container.addEventListener('click', this.handleClick.bind(this));}register(selector, handler) {if (!this.handlers.has(selector)) {this.handlers.set(selector, []);}this.handlers.get(selector).push(handler);}handleClick(event) {this.handlers.forEach((handlers, selector) => {if (event.target.matches(selector)) {handlers.forEach(handler => handler(event));}});}
}// 使用示例
const delegator = new EventDelegator(document.body);
delegator.register('.button', (e) => console.log('Button clicked'));
delegator.register('.link', (e) => console.log('Link clicked'));
防抖和节流
// 防抖函数
function debounce(func, wait, immediate = false) {let timeout;return function executedFunction(...args) {const later = () => {timeout = null;if (!immediate) func.apply(this, args);};const callNow = immediate && !timeout;clearTimeout(timeout);timeout = setTimeout(later, wait);if (callNow) func.apply(this, args);};
}// 节流函数
function throttle(func, limit) {let inThrottle;return function(...args) {if (!inThrottle) {func.apply(this, args);inThrottle = true;setTimeout(() => inThrottle = false, limit);}};
}// 使用示例
const debouncedSearch = debounce((query) => {// 执行搜索
}, 300);const throttledScroll = throttle(() => {// 处理滚动
}, 100);window.addEventListener('scroll', throttledScroll);
网络请求优化
1. HTTP 优化
请求合并与缓存
// 请求缓存管理器
class RequestCacheManager {constructor() {this.cache = new Map();this.pendingRequests = new Map();}async get(url, options = {}) {const cacheKey = this.getCacheKey(url, options);// 检查缓存if (this.cache.has(cacheKey)) {const cached = this.cache.get(cacheKey);if (!this.isExpired(cached)) {return cached.data;}}// 检查是否有pending请求if (this.pendingRequests.has(cacheKey)) {return this.pendingRequests.get(cacheKey);}// 发起新请求const request = this.makeRequest(url, options);this.pendingRequests.set(cacheKey, request);try {const data = await request;this.cache.set(cacheKey, {data,timestamp: Date.now(),ttl: options.ttl || 60000 // 默认1分钟});return data;} finally {this.pendingRequests.delete(cacheKey);}}getCacheKey(url, options) {return `${url}:${JSON.stringify(options)}`;}isExpired(cached) {return Date.now() - cached.timestamp > cached.ttl;}async makeRequest(url, options) {const response = await fetch(url, options);return response.json();}
}
批量请求优化
// 请求批处理器
class BatchRequestProcessor {constructor(batchSize = 10, delay = 100) {this.batchSize = batchSize;this.delay = delay;this.queue = [];this.processing = false;}add(request) {return new Promise((resolve, reject) => {this.queue.push({ request, resolve, reject });this.process();});}async process() {if (this.processing || this.queue.length === 0) return;this.processing = true;while (this.queue.length > 0) {const batch = this.queue.splice(0, this.batchSize);try {const promises = batch.map(({ request }) => request());const results = await Promise.allSettled(promises);results.forEach((result, index) => {const { resolve, reject } = batch[index];if (result.status === 'fulfilled') {resolve(result.value);} else {reject(result.reason);}});} catch (error) {batch.forEach(({ reject }) => reject(error));}// 批次间延迟if (this.queue.length > 0) {await new Promise(resolve => setTimeout(resolve, this.delay));}}this.processing = false;}
}
2. 数据预加载
智能预加载策略
// 路由预加载器
class RoutePreloader {constructor() {this.preloadedRoutes = new Set();this.observer = new IntersectionObserver(this.onLinkVisible.bind(this),{ rootMargin: '100px' });this.init();}init() {// 监听所有路由链接const links = document.querySelectorAll('a[href^="/"]');links.forEach(link => this.observer.observe(link));// 鼠标悬停预加载document.addEventListener('mouseover', this.onLinkHover.bind(this));}onLinkVisible(entries) {entries.forEach(entry => {if (entry.isIntersecting) {const href = entry.target.getAttribute('href');this.preloadRoute(href);}});}onLinkHover(event) {if (event.target.tagName === 'A' && event.target.href) {const href = new URL(event.target.href).pathname;this.preloadRoute(href);}}async preloadRoute(href) {if (this.preloadedRoutes.has(href)) return;this.preloadedRoutes.add(href);try {// 预加载路由组件const module = await import(/* webpackChunkName: "[request]" */ `./views${href}`);console.log(`预加载路由: ${href}`);} catch (error) {console.warn(`预加载路由失败: ${href}`, error);this.preloadedRoutes.delete(href);}}
}
构建与部署优化
1. Webpack 构建优化
配置优化示例
// webpack.config.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');module.exports = {mode: 'production',// 入口优化entry: {app: './src/main.js',vendor: ['react', 'react-dom', 'lodash'] // 提取第三方库},// 输出优化output: {path: path.resolve(__dirname, 'dist'),filename: '[name].[contenthash:8].js',chunkFilename: '[name].[contenthash:8].chunk.js',clean: true},// 优化配置optimization: {// 代码分割splitChunks: {chunks: 'all',cacheGroups: {vendor: {test: /[\\/]node_modules[\\/]/,name: 'vendors',chunks: 'all',priority: 10},common: {name: 'common',minChunks: 2,chunks: 'all',priority: 5}}},// 压缩优化minimizer: [new TerserPlugin({terserOptions: {compress: {drop_console: true, // 移除consoledrop_debugger: true // 移除debugger}},parallel: true // 并行压缩})],// 运行时代码提取runtimeChunk: 'single'},// 插件配置plugins: [// Gzip压缩new CompressionPlugin({algorithm: 'gzip',test: /\.(js|css|html|svg)$/,threshold: 8192,minRatio: 0.8}),// 包分析process.env.ANALYZE && new BundleAnalyzerPlugin()].filter(Boolean),// 模块解析优化resolve: {alias: {'@': path.resolve(__dirname, 'src')},extensions: ['.js', '.jsx', '.ts', '.tsx'],modules: ['node_modules'] // 限制模块查找范围},// 外部化依赖externals: {'react': 'React','react-dom': 'ReactDOM'}
};
2. 现代构建工具优化
Vite 配置优化
// vite.config.js
import { defineConfig } from 'vite';
import { resolve } from 'path';export default defineConfig({// 构建优化build: {target: 'es2015',minify: 'terser',terserOptions: {compress: {drop_console: true,drop_debugger: true}},// 代码分割rollupOptions: {output: {manualChunks: {vendor: ['react', 'react-dom'],utils: ['lodash', 'dayjs']}}},// 资源大小限制chunkSizeWarningLimit: 1000},// 开发服务器优化server: {hmr: {overlay: false // 关闭错误遮罩层}},// 插件配置plugins: [// ... 插件配置],// 依赖预构建optimizeDeps: {include: ['react', 'react-dom'],exclude: ['@vueuse/core']}
});
3. CDN 与缓存策略
缓存策略配置
# nginx 配置示例
server {listen 80;server_name example.com;# 静态资源缓存location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {expires 1y;add_header Cache-Control "public, immutable";add_header Vary "Accept-Encoding";# 启用 gzipgzip on;gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;}# HTML 文件缓存策略location ~* \.(html|htm)$ {expires 1h;add_header Cache-Control "public, must-revalidate";}# API 缓存location /api/ {proxy_pass http://backend;proxy_cache my_cache;proxy_cache_valid 200 5m;proxy_cache_key $request_uri;}
}
监控与持续优化
1. 性能监控实现
Real User Monitoring (RUM)
// 性能监控类
class PerformanceMonitor {constructor(apiUrl) {this.apiUrl = apiUrl;this.metrics = {};this.init();}init() {// 监听页面加载完成window.addEventListener('load', () => {this.collectPageMetrics();});// 监听 CLSthis.observeCLS();// 监听 LCPthis.observeLCP();// 监听 FIDthis.observeFID();// 定期发送数据setInterval(() => this.sendMetrics(), 30000);}collectPageMetrics() {const navigation = performance.getEntriesByType('navigation')[0];this.metrics = {...this.metrics,dns: navigation.domainLookupEnd - navigation.domainLookupStart,tcp: navigation.connectEnd - navigation.connectStart,request: navigation.responseStart - navigation.requestStart,response: navigation.responseEnd - navigation.responseStart,dom: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,load: navigation.loadEventEnd - navigation.loadEventStart,fcp: this.getFCP(),ttfb: navigation.responseStart - navigation.requestStart};}observeCLS() {let clsValue = 0;let clsEntries = [];const observer = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {if (!entry.hadRecentInput) {clsValue += entry.value;clsEntries.push(entry);}}this.metrics.cls = clsValue;});observer.observe({ type: 'layout-shift', buffered: true });}observeLCP() {const observer = new PerformanceObserver((list) => {const entries = list.getEntries();const lastEntry = entries[entries.length - 1];this.metrics.lcp = lastEntry.startTime;});observer.observe({ type: 'largest-contentful-paint', buffered: true });}observeFID() {const observer = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {this.metrics.fid = entry.processingStart - entry.startTime;}});observer.observe({ type: 'first-input', buffered: true });}getFCP() {const fcpEntry = performance.getEntriesByName('first-contentful-paint')[0];return fcpEntry ? fcpEntry.startTime : null;}async sendMetrics() {if (Object.keys(this.metrics).length === 0) return;try {await fetch(this.apiUrl, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({url: location.href,userAgent: navigator.userAgent,timestamp: Date.now(),metrics: this.metrics})});} catch (error) {console.error('发送监控数据失败:', error);}}
}// 初始化监控
new PerformanceMonitor('/api/metrics');
2. 错误监控
错误收集与上报
class ErrorMonitor {constructor(apiUrl) {this.apiUrl = apiUrl;this.errorQueue = [];this.maxQueueSize = 10;this.init();}init() {// JavaScript 错误监听window.addEventListener('error', this.handleError.bind(this));// Promise 未捕获错误window.addEventListener('unhandledrejection', this.handlePromiseError.bind(this));// 资源加载错误window.addEventListener('error', this.handleResourceError.bind(this), true);// 定期发送错误setInterval(() => this.flushErrors(), 10000);}handleError(event) {this.addError({type: 'javascript',message: event.message,filename: event.filename,lineno: event.lineno,colno: event.colno,stack: event.error?.stack,timestamp: Date.now()});}handlePromiseError(event) {this.addError({type: 'promise',message: event.reason.toString(),stack: event.reason.stack,timestamp: Date.now()});}handleResourceError(event) {if (event.target !== window) {this.addError({type: 'resource',message: `Failed to load ${event.target.tagName}`,source: event.target.src || event.target.href,timestamp: Date.now()});}}addError(error) {this.errorQueue.push(error);if (this.errorQueue.length >= this.maxQueueSize) {this.flushErrors();}}async flushErrors() {if (this.errorQueue.length === 0) return;const errors = [...this.errorQueue];this.errorQueue = [];try {await fetch(this.apiUrl, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({url: location.href,userAgent: navigator.userAgent,errors})});} catch (error) {// 发送失败,重新加入队列this.errorQueue.unshift(...errors);}}
}
面试回答技巧
1. 回答框架
STAR 方法
- Situation: 描述项目背景和遇到的性能问题
- Task: 说明你的任务和目标
- Action: 详细描述你采取的优化措施
- Result: 量化优化结果和效果
2. 常见面试问题及回答要点
问题1: 如何分析老旧项目的性能问题?
回答要点:
1. 性能指标分析- 使用 Lighthouse 进行综合评估- Chrome DevTools 分析加载时间线- 监控 Core Web Vitals 指标2. 代码质量审查- 分析 bundle 大小和组成- 检查无用代码和重复依赖- 评估代码分割情况3. 网络性能分析- 检查资源加载策略- 分析缓存配置- 评估 CDN 使用情况4. 运行时性能分析- 使用 Performance 面板分析主线程阻塞- 检查内存使用和泄漏- 分析重排重绘频率
问题2: 具体实施了哪些优化措施?
回答示例:
我负责的项目是一个电商后台管理系统,页面加载时间超过8秒,用户体验很差。优化措施:1. 构建优化 (40% 性能提升)- 实施代码分割,将第三方库单独打包- 启用 Tree Shaking 删除无用代码- 配置 Gzip 压缩,资源大小减少60%2. 资源优化 (30% 性能提升)- 图片格式现代化,使用 WebP- 实施图片懒加载- 字体预加载和子集化3. 代码优化 (20% 性能提升)- 实施虚拟滚动解决长列表性能问题- 使用 React.memo 和 useMemo 减少不必要渲染- 优化事件处理,使用防抖节流4. 缓存策略 (10% 性能提升)- 配置合理的 HTTP 缓存策略- 实施接口缓存和预加载结果:页面加载时间从8秒降到2.5秒,Lighthouse 分数从30分提升到85分。
问题3: 如何保证优化效果的持续性?
回答要点:
1. 性能监控体系- 部署 RUM 监控收集真实用户数据- 设置性能预警阈值- 定期生成性能报告2. 开发流程集成- CI/CD 中集成 Lighthouse 检查- Bundle 分析报告- 性能回归测试3. 团队规范建立- 制定性能开发规范- 代码审查中关注性能- 定期技术分享和培训4. 持续优化机制- 月度性能回顾会议- 新技术调研和应用- 用户反馈收集和处理
3. 面试加分技巧
展示技术深度
- 能够解释优化原理,不只是列举方法
- 提及新技术和最佳实践的应用
- 展示对浏览器工作原理的理解
展示业务思维
- 将技术优化与业务价值关联
- 提及用户体验改善和转化率提升
- 展示成本效益分析能力
展示团队协作
- 说明如何推动团队采用新的优化方案
- 分享知识传递和培训经验
- 展示跨部门沟通协调能力
实战案例分析
案例1: 电商网站首页优化
优化前状况:
- 首屏加载时间: 6.8秒
- Lighthouse 分数: 32分
- 资源总大小: 3.2MB
- JavaScript 执行时间: 2.1秒
优化措施:
- 关键资源优化
// 首屏关键CSS内联
const criticalCSS = `.header { height: 60px; background: #fff; }.hero { height: 400px; background: #f5f5f5; }
`;// 非关键CSS异步加载
const loadCSS = (href) => {const link = document.createElement('link');link.rel = 'preload';link.as = 'style';link.href = href;link.onload = () => link.rel = 'stylesheet';document.head.appendChild(link);
};
- 图片优化
<!-- 首屏轮播图优化 -->
<picture><source srcset="hero-mobile.webp" media="(max-width: 768px)" type="image/webp"><source srcset="hero-desktop.webp" type="image/webp"><img src="hero-desktop.jpg" alt="首页轮播" loading="eager">
</picture>
- JavaScript 优化
// 路由懒加载
const routes = [{path: '/product/:id',component: () => import(/* webpackChunkName: "product" */ './Product.vue')}
];// 第三方脚本延迟加载
const loadScript = (src, defer = true) => {const script = document.createElement('script');script.src = src;script.defer = defer;document.head.appendChild(script);
};// 页面加载完成后加载非关键脚本
window.addEventListener('load', () => {loadScript('/js/analytics.js');loadScript('/js/chat-widget.js');
});
优化结果:
- 首屏加载时间: 2.1秒 (提升69%)
- Lighthouse 分数: 89分 (提升178%)
- 资源总大小: 1.8MB (减少44%)
- 用户转化率提升15%
案例2: 后台管理系统优化
优化前问题:
- 列表页渲染1000条数据卡顿严重
- 内存占用持续增长,存在泄漏
- 频繁的接口请求导致服务器压力大
优化方案:
- 虚拟滚动实现
<template><div class="virtual-list" ref="container" @scroll="onScroll"><div class="virtual-list-phantom" :style="{ height: totalHeight + 'px' }"></div><div class="virtual-list-content" :style="getContentStyle()"><divv-for="item in visibleItems":key="item.id"class="virtual-list-item":style="{ height: itemHeight + 'px' }"><slot :item="item"></slot></div></div></div>
</template><script>
export default {props: {items: Array,itemHeight: { type: Number, default: 50 }},data() {return {startIndex: 0,endIndex: 0,containerHeight: 0};},computed: {totalHeight() {return this.items.length * this.itemHeight;},visibleCount() {return Math.ceil(this.containerHeight / this.itemHeight) + 2;},visibleItems() {return this.items.slice(this.startIndex, this.endIndex);}},methods: {onScroll() {const scrollTop = this.$refs.container.scrollTop;this.startIndex = Math.floor(scrollTop / this.itemHeight);this.endIndex = this.startIndex + this.visibleCount;},getContentStyle() {return {transform: `translateY(${this.startIndex * this.itemHeight}px)`};}}
};
</script>
- 内存泄漏修复
// 组件清理机制
export default {data() {return {timers: [],observers: [],subscriptions: []};},methods: {addTimer(id) {this.timers.push(id);},addObserver(observer) {this.observers.push(observer);},addSubscription(subscription) {this.subscriptions.push(subscription);}},beforeDestroy() {// 清理定时器this.timers.forEach(clearInterval);// 清理观察者this.observers.forEach(observer => observer.disconnect());// 清理订阅this.subscriptions.forEach(sub => sub.unsubscribe());}
};
- 接口请求优化
// 请求去重和缓存
class APIManager {constructor() {this.cache = new Map();this.pendingRequests = new Map();}async request(url, options = {}) {const key = `${url}:${JSON.stringify(options)}`;// 检查缓存if (this.cache.has(key)) {const cached = this.cache.get(key);if (Date.now() - cached.timestamp < 30000) {return cached.data;}}// 防止重复请求if (this.pendingRequests.has(key)) {return this.pendingRequests.get(key);}const request = fetch(url, options).then(res => res.json());this.pendingRequests.set(key, request);try {const data = await request;this.cache.set(key, { data, timestamp: Date.now() });return data;} finally {this.pendingRequests.delete(key);}}
}
优化结果:
- 列表渲染性能提升90%,支持10万条数据流畅滚动
- 内存使用稳定,无泄漏现象
- 接口请求减少70%,服务器响应时间提升50%
总结
老旧项目性能优化是一个系统工程,需要从分析、优化、监控三个维度进行全面考虑:
关键成功因素
- 全面的性能分析 - 使用合适的工具找出真正的性能瓶颈
- 分层次的优化策略 - 从构建到运行时的全链路优化
- 持续的监控体系 - 确保优化效果的可持续性
- 团队协作和知识传承 - 让优化成果在团队中延续
面试要点
- 体现技术深度和实战经验
- 强调业务价值和用户体验改善
- 展示系统思维和持续改进能力
- 准备具体的数据和案例支撑
性能优化不仅是技术问题,更是工程问题。在面试中展现你的全面思考能力,会让你在众多候选人中脱颖而出。