前端性能优化终极指南

概述

在当今快节奏的互联网环境中,网站性能直接影响用户体验和业务成果。研究表明,页面加载时间每增加1秒,转化率就可能下降7%。本指南将为你提供15个经过实战验证的前端性能优化技巧。

性能优化的重要性

用户体验影响:

  • 53%的用户会在页面加载超过3秒时放弃访问
  • 1秒的延迟可能导致转化率下降7%
  • 79%的用户对性能不佳的网站不会再访问

业务影响:

  • Google将页面速度作为搜索排名因素
  • 快速的网站能获得更好的SEO排名
  • 优化后的网站能显著提升用户留存率

核心性能指标

Web Vitals指标

Largest Contentful Paint (LCP):最大内容绘制时间

1
2
3
4
5
6
// 测量LCP
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('LCP:', entry.startTime);
  }
}).observe({entryTypes: ['largest-contentful-paint']});

First Input Delay (FID):首次输入延迟

1
2
3
4
5
6
// 测量FID
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('FID:', entry.processingStart - entry.startTime);
  }
}).observe({entryTypes: ['first-input']});

Cumulative Layout Shift (CLS):累积布局偏移

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 测量CLS
let clsValue = 0;
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
    }
  }
  console.log('CLS:', clsValue);
}).observe({entryTypes: ['layout-shift']});

1. 资源优化策略

图片优化

WebP格式转换:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 图片格式检测和转换
function optimizeImage(src, format = 'webp') {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => {
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;

      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0);

      canvas.toBlob((blob) => {
        const url = URL.createObjectURL(blob);
        resolve(url);
      }, `image/${format}`, 0.8);
    };
    img.src = src;
  });
}

响应式图片:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<picture>
  <source
    srcset="image-800w.webp 800w, image-1200w.webp 1200w"
    sizes="(max-width: 600px) 480px, 800px"
    type="image/webp"
  >
  <source
    srcset="image-800w.jpg 800w, image-1200w.jpg 1200w"
    sizes="(max-width: 600px) 480px, 800px"
    type="image/jpeg"
  >
  <img
    src="image-800w.jpg"
    alt="优化后的图片"
    loading="lazy"
    width="800"
    height="600"
  >
</picture>

图片懒加载实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Intersection Observer实现图片懒加载
const imageObserver = 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);
    }
  });
});

document.querySelectorAll('img[data-src]').forEach(img => {
  imageObserver.observe(img);
});

字体优化

字体预加载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!-- 预加载关键字体 -->
<link
  rel="preload"
  href="/fonts/main.woff2"
  as="font"
  type="font/woff2"
  crossorigin
>

<!-- 字体显示策略 -->
<style>
  @font-face {
    font-family: 'Custom Font';
    src: url('/fonts/main.woff2') format('woff2');
    font-display: swap; /* 避免字体加载延迟 */
  }
</style>

字体子集化:

1
2
3
4
5
6
7
// 使用fontkit进行字体子集化
const fontSubset = {
  // 只包含中文字符
  unicodeRange: 'U+4E00-U+9FFF',
  // 英文字符
  unicodeRange: 'U+0000-U+007F'
};

2. 代码优化技术

JavaScript优化

代码分割:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 动态导入实现代码分割
async function loadModule() {
  const module = await import('./heavy-module.js');
  module.doSomething();
}

// 路由级别的代码分割
const routes = [
  {
    path: '/dashboard',
    component: () => import('./views/Dashboard.vue')
  },
  {
    path: '/profile',
    component: () => import('./views/Profile.vue')
  }
];

Tree Shaking优化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 确保ES6模块导入
import { debounce, throttle } from 'lodash-es';
// 而不是 import _ from 'lodash';

// 使用webpack的sideEffects配置
// package.json
{
  "sideEffects": [
    "*.css",
    "./src/style/**"
  ]
}

防抖和节流:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 防抖函数
function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

// 节流函数
function throttle(func, limit) {
  let inThrottle;
  return function() {
    const args = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// 使用示例
const handleScroll = throttle(() => {
  console.log('滚动事件处理');
}, 100);

const handleResize = debounce(() => {
  console.log('窗口大小改变');
}, 250);

CSS优化

CSS压缩和优化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// PostCSS配置示例
module.exports = {
  plugins: [
    require('cssnano')({
      preset: 'default'
    }),
    require('autoprefixer'),
    require('@fullhuman/postcss-purgecss')({
      content: ['./src/**/*.{js,jsx,ts,tsx,html}'],
      defaultExtractor: content => {
        const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || []
        const innerMatches = content.match(/[^<>"'`\s.()]*[^<>"'`\s.():]/g) || []
        return broadMatches.concat(innerMatches)
      }
    })
  ]
}

关键CSS内联:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 提取关键CSS
const criticalCSS = `
  body { margin: 0; font-family: Arial, sans-serif; }
  .header { background: #333; color: white; padding: 1rem; }
  .content { max-width: 1200px; margin: 0 auto; padding: 1rem; }
`;

// 内联到HTML头部
const html = `
<!DOCTYPE html>
<html>
<head>
  <style>${criticalCSS}</style>
  <link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>
<body>
  <!-- 页面内容 -->
</body>
</html>
`;

3. 缓存策略

浏览器缓存

Cache-Control配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Express.js示例
app.use(express.static('public', {
  maxAge: '1y', // 静态资源缓存1年
  etag: true,
  lastModified: true
}));

// 不同类型资源的缓存策略
app.use((req, res, next) => {
  if (req.url.endsWith('.html')) {
    res.setHeader('Cache-Control', 'no-cache');
  } else if (req.url.match(/\.(js|css)$/)) {
    res.setHeader('Cache-Control', 'max-age=31536000'); // 1年
  } else if (req.url.match(/\.(jpg|jpeg|png|gif|webp)$/)) {
    res.setHeader('Cache-Control', 'max-age=2592000'); // 30天
  }
  next();
});

Service Worker缓存:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Service Worker缓存策略
const CACHE_NAME = 'app-cache-v1';
const urlsToCache = [
  '/',
  '/styles/main.css',
  '/scripts/main.js',
  '/images/logo.png'
];

// 安装阶段
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(urlsToCache))
  );
});

// 拦截请求
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // 缓存命中,返回缓存的资源
        if (response) {
          return response;
        }

        // 缓存未命中,发起网络请求
        return fetch(event.request).then(response => {
          // 检查是否是有效响应
          if (!response || response.status !== 200 || response.type !== 'basic') {
            return response;
          }

          // 克隆响应用于缓存
          const responseToCache = response.clone();

          caches.open(CACHE_NAME)
            .then(cache => {
              cache.put(event.request, responseToCache);
            });

          return response;
        });
      })
  );
});

本地存储优化

IndexedDB大数据存储:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// IndexedDB封装类
class StorageManager {
  constructor(dbName = 'AppDB', version = 1) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
  }

  async init() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve(this.db);
      };

      request.onupgradeneeded = event => {
        const db = event.target.result;

        if (!db.objectStoreNames.contains('cache')) {
          db.createObjectStore('cache', { keyPath: 'id' });
        }
      };
    });
  }

  async set(key, data, ttl = 3600000) { // 默认1小时过期
    const item = {
      id: key,
      data: data,
      timestamp: Date.now(),
      ttl: ttl
    };

    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['cache'], 'readwrite');
      const store = transaction.objectStore('cache');
      const request = store.put(item);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async get(key) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['cache'], 'readonly');
      const store = transaction.objectStore('cache');
      const request = store.get(key);

      request.onsuccess = () => {
        const item = request.result;
        if (!item) {
          resolve(null);
          return;
        }

        // 检查是否过期
        if (Date.now() - item.timestamp > item.ttl) {
          this.delete(key);
          resolve(null);
          return;
        }

        resolve(item.data);
      };

      request.onerror = () => reject(request.error);
    });
  }

  async delete(key) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['cache'], 'readwrite');
      const store = transaction.objectStore('cache');
      const request = store.delete(key);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
}

4. 网络优化

HTTP/2优化

服务器推送:

1
2
3
4
5
# Nginx配置示例
location = /style.css {
    http2_push /style.css;
    http2_push /script.js;
}

资源合并和压缩:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Webpack配置优化
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
        common: {
          name: 'common',
          minChunks: 2,
          chunks: 'all',
          enforce: true
        }
      }
    },
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true,
          },
        },
      }),
    ]
  }
}

CDN加速

静态资源CDN配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 构建时配置CDN
const cdnConfig = {
  development: {
    publicPath: '/',
  },
  production: {
    publicPath: 'https://cdn.yourdomain.com/'
  }
};

// 动态资源CDN
function getCdnUrl(path) {
  const cdnHosts = [
    'https://cdn1.yourdomain.com',
    'https://cdn2.yourdomain.com',
    'https://cdn3.yourdomain.com'
  ];

  const hash = path.split('').reduce((a, b) => {
    a = ((a << 5) - a) + b.charCodeAt(0);
    return a & a;
  }, 0);

  const index = Math.abs(hash) % cdnHosts.length;
  return cdnHosts[index] + path;
}

5. 渲染优化

虚拟滚动

虚拟滚动组件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 虚拟滚动实现
class VirtualScroll {
  constructor(container, itemHeight, renderItem) {
    this.container = container;
    this.itemHeight = itemHeight;
    this.renderItem = renderItem;
    this.data = [];
    this.visibleStart = 0;
    this.visibleEnd = 0;
    this.scrollTop = 0;

    this.init();
  }

  init() {
    this.container.addEventListener('scroll', this.handleScroll.bind(this));
    this.updateVisibleRange();
  }

  handleScroll() {
    this.scrollTop = this.container.scrollTop;
    this.updateVisibleRange();
    this.render();
  }

  updateVisibleRange() {
    const containerHeight = this.container.clientHeight;
    const startIndex = Math.floor(this.scrollTop / this.itemHeight);
    const endIndex = Math.ceil((this.scrollTop + containerHeight) / this.itemHeight);

    // 添加缓冲区
    const bufferSize = 5;
    this.visibleStart = Math.max(0, startIndex - bufferSize);
    this.visibleEnd = Math.min(this.data.length, endIndex + bufferSize);
  }

  render() {
    const fragment = document.createDocumentFragment();

    // 创建占位空间
    const spacerBefore = document.createElement('div');
    spacerBefore.style.height = `${this.visibleStart * this.itemHeight}px`;
    fragment.appendChild(spacerBefore);

    // 渲染可见项
    for (let i = this.visibleStart; i < this.visibleEnd; i++) {
      const item = this.renderItem(this.data[i], i);
      item.style.height = `${this.itemHeight}px`;
      item.style.position = 'absolute';
      item.style.top = `${i * this.itemHeight}px`;
      item.style.width = '100%';
      fragment.appendChild(item);
    }

    // 创建底部占位空间
    const spacerAfter = document.createElement('div');
    spacerAfter.style.height = `${(this.data.length - this.visibleEnd) * this.itemHeight}px`;
    fragment.appendChild(spacerAfter);

    this.container.innerHTML = '';
    this.container.appendChild(fragment);
  }

  setData(data) {
    this.data = data;
    this.updateVisibleRange();
    this.render();
  }
}

骨架屏加载

骨架屏组件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 骨架屏生成器
class SkeletonLoader {
  constructor(container) {
    this.container = container;
  }

  generateCardSkeleton() {
    return `
      <div class="skeleton-card">
        <div class="skeleton-avatar"></div>
        <div class="skeleton-content">
          <div class="skeleton-title"></div>
          <div class="skeleton-text"></div>
          <div class="skeleton-text short"></div>
        </div>
      </div>
    `;
  }

  generateListSkeleton(count = 5) {
    let skeleton = '';
    for (let i = 0; i < count; i++) {
      skeleton += this.generateCardSkeleton();
    }
    return skeleton;
  }

  show() {
    this.container.innerHTML = this.generateListSkeleton();
  }

  hide(content) {
    this.container.innerHTML = content;
  }
}

// CSS样式
const skeletonCSS = `
.skeleton-card {
  display: flex;
  padding: 16px;
  border: 1px solid #eee;
  margin-bottom: 8px;
}

.skeleton-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-loading 1.5s infinite;
}

.skeleton-title {
  height: 20px;
  width: 60%;
  margin-bottom: 8px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-loading 1.5s infinite;
}

.skeleton-text {
  height: 16px;
  width: 100%;
  margin-bottom: 4px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-loading 1.5s infinite;
}

.skeleton-text.short {
  width: 40%;
}

@keyframes skeleton-loading {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
`;

6. 性能监控

性能数据收集

自定义性能监控:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
class PerformanceMonitor {
  constructor() {
    this.metrics = {};
    this.observers = [];
  }

  startMonitoring() {
    // 监控页面加载时间
    window.addEventListener('load', () => {
      this.collectLoadMetrics();
    });

    // 监控用户交互性能
    this.monitorUserInteractions();

    // 监控资源加载
    this.monitorResourceLoading();
  }

  collectLoadMetrics() {
    const navigation = performance.getEntriesByType('navigation')[0];

    this.metrics = {
      dns: navigation.domainLookupEnd - navigation.domainLookupStart,
      tcp: navigation.connectEnd - navigation.connectStart,
      ssl: navigation.secureConnectionStart > 0 ?
           navigation.connectEnd - navigation.secureConnectionStart : 0,
      ttfb: navigation.responseStart - navigation.requestStart,
      download: navigation.responseEnd - navigation.responseStart,
      domParse: navigation.domContentLoadedEventStart - navigation.responseEnd,
      domReady: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
      load: navigation.loadEventEnd - navigation.loadEventStart
    };

    this.sendMetrics();
  }

  monitorUserInteractions() {
    // 监控点击事件
    document.addEventListener('click', (event) => {
      const startTime = performance.now();

      // 使用setTimeout等待事件处理完成
      setTimeout(() => {
        const endTime = performance.now();
        this.recordInteraction('click', endTime - startTime);
      }, 0);
    });
  }

  monitorResourceLoading() {
    const observer = new PerformanceObserver((list) => {
      list.getEntries().forEach((entry) => {
        if (entry.entryType === 'resource') {
          this.recordResourceMetric(entry);
        }
      });
    });

    observer.observe({ entryTypes: ['resource'] });
  }

  recordResourceMetric(entry) {
    const resourceMetrics = {
      name: entry.name,
      type: this.getResourceType(entry.name),
      size: entry.transferSize,
      duration: entry.duration,
      cached: entry.transferSize === 0 && entry.decodedBodySize > 0
    };

    // 发送资源指标
    this.sendResourceMetrics(resourceMetrics);
  }

  getResourceType(url) {
    if (url.match(/\.(css)$/)) return 'css';
    if (url.match(/\.(js)$/)) return 'javascript';
    if (url.match(/\.(png|jpg|jpeg|gif|webp)$/)) return 'image';
    if (url.match(/\.(woff|woff2|ttf)$/)) return 'font';
    return 'other';
  }

  sendMetrics() {
    // 发送到分析服务
    fetch('/api/performance-metrics', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        url: window.location.href,
        userAgent: navigator.userAgent,
        timestamp: Date.now(),
        metrics: this.metrics
      })
    }).catch(error => {
      console.error('Failed to send performance metrics:', error);
    });
  }
}

实时性能仪表板

性能仪表板组件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class PerformanceDashboard {
  constructor(container) {
    this.container = container;
    this.charts = {};
    this.init();
  }

  init() {
    this.render();
    this.startRealTimeUpdates();
  }

  render() {
    this.container.innerHTML = `
      <div class="dashboard">
        <div class="dashboard-header">
          <h2>性能监控仪表板</h2>
          <div class="last-update">最后更新: <span id="lastUpdate">--</span></div>
        </div>

        <div class="metrics-grid">
          <div class="metric-card">
            <h3>页面加载时间</h3>
            <div class="metric-value" id="loadTime">--</div>
            <div class="metric-unit">ms</div>
          </div>

          <div class="metric-card">
            <h3>首次内容绘制</h3>
            <div class="metric-value" id="fcp">--</div>
            <div class="metric-unit">ms</div>
          </div>

          <div class="metric-card">
            <h3>最大内容绘制</h3>
            <div class="metric-value" id="lcp">--</div>
            <div class="metric-unit">ms</div>
          </div>

          <div class="metric-card">
            <h3>累积布局偏移</h3>
            <div class="metric-value" id="cls">--</div>
            <div class="metric-unit">score</div>
          </div>
        </div>

        <div class="charts-section">
          <div class="chart-container">
            <h3>加载时间趋势</h3>
            <canvas id="loadTimeChart"></canvas>
          </div>

          <div class="chart-container">
            <h3>资源加载分析</h3>
            <canvas id="resourceChart"></canvas>
          </div>
        </div>
      </div>
    `;
  }

  updateMetrics(metrics) {
    document.getElementById('loadTime').textContent = metrics.loadTime;
    document.getElementById('fcp').textContent = metrics.fcp;
    document.getElementById('lcp').textContent = metrics.lcp;
    document.getElementById('cls').textContent = metrics.cls.toFixed(3);
    document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString();
  }

  startRealTimeUpdates() {
    // 每5秒更新一次数据
    setInterval(() => {
      this.fetchLatestMetrics();
    }, 5000);
  }

  fetchLatestMetrics() {
    fetch('/api/performance/latest')
      .then(response => response.json())
      .then(data => {
        this.updateMetrics(data);
        this.updateCharts(data);
      })
      .catch(error => {
        console.error('Failed to fetch metrics:', error);
      });
  }
}

性能优化检查清单

发布前检查

  • 图片优化

    • 使用WebP格式
    • 实现响应式图片
    • 添加懒加载
    • 压缩图片大小
  • 代码优化

    • 启用代码压缩
    • 实现代码分割
    • 移除未使用的代码
    • 优化依赖包大小
  • 缓存策略

    • 配置浏览器缓存
    • 实现Service Worker
    • 使用CDN加速
    • 优化缓存策略
  • 网络优化

    • 启用Gzip压缩
    • 使用HTTP/2
    • 减少HTTP请求
    • 优化DNS查询

持续监控

  • 性能指标监控

    • 页面加载时间
    • 首次内容绘制
    • 最大内容绘制
    • 累积布局偏移
  • 用户体验监控

    • 用户交互响应时间
    • 错误率统计
    • 用户行为分析
    • 设备兼容性

常见性能问题解决

问题1:JavaScript阻塞渲染

症状:页面长时间白屏

解决方案:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 使用async和defer属性
<script src="non-critical.js" async></script>
<script src="non-critical.js" defer></script>

// 动态加载脚本
function loadScript(src, callback) {
  const script = document.createElement('script');
  script.src = src;
  script.onload = callback;
  document.head.appendChild(script);
}

问题2:CSS阻塞渲染

症状:页面样式加载缓慢

解决方案:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!-- 关键CSS内联 -->
<style>
  /* 关键样式 */
  body { margin: 0; font-family: Arial; }
  .header { background: #333; }
</style>

<!-- 非关键CSS异步加载 -->
<link
  rel="preload"
  href="styles.css"
  as="style"
  onload="this.onload=null;this.rel='stylesheet'"
>

问题3:内存泄漏

症状:页面使用时间越长越卡

解决方案:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 清理事件监听器
function cleanup() {
  window.removeEventListener('scroll', handleScroll);
  window.removeEventListener('resize', handleResize);

  // 清理定时器
  clearInterval(timerId);
  clearTimeout(timeoutId);

  // 清理DOM引用
  element = null;
}

// 页面卸载时清理
window.addEventListener('beforeunload', cleanup);

工具和资源推荐

性能测试工具

  1. Google PageSpeed Insights

    • 网址:https://pagespeed.web.dev/
    • 功能:综合性能分析,提供优化建议
  2. WebPageTest

    • 网址:https://www.webpagetest.org/
    • 功能:详细的性能测试和瀑布图分析
  3. Lighthouse

    • Chrome DevTools内置
    • 功能:全面的质量和性能审计

开发工具

  1. Bundle Analyzer

    1
    
    npm install --save-dev webpack-bundle-analyzer
    
  2. 性能监控库

    1
    2
    3
    4
    5
    6
    7
    8
    
    // Web Vitals库
    import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';
    
    getCLS(console.log);
    getFID(console.log);
    getFCP(console.log);
    getLCP(console.log);
    getTTFB(console.log);
    
  3. 性能预算工具

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    // webpack性能预算配置
    module.exports = {
     performance: {
       maxAssetSize: 244 * 1024, // 244KB
       maxEntrypointSize: 244 * 1024,
       hints: "warning", // 或 "error"
       assetFilter: function(assetFilename) {
         return assetFilename.endsWith('.js') || assetFilename.endsWith('.css');
       },
     }
    }
    

总结

前端性能优化是一个持续的过程,需要从多个维度综合考虑。通过本文介绍的15个优化技巧,你可以显著提升Web应用的性能:

关键要点

  1. 资源优化:图片压缩、代码分割、缓存策略
  2. 网络优化:CDN加速、HTTP/2、减少请求
  3. 渲染优化:虚拟滚动、骨架屏、懒加载
  4. 监控优化:性能指标、实时监控、问题排查

持续改进

  • 定期进行性能审计
  • 跟踪最新的Web性能最佳实践
  • 根据用户反馈持续优化
  • 建立性能监控和告警机制

记住,性能优化没有终点,只有不断改进和优化。从今天开始,将这些技巧应用到你的项目中,为用户提供更快的体验!


相关资源

分享你的优化经验:欢迎在评论区分享你的性能优化技巧和经验!