前端性能优化

性能优化不应是事后的补救措施,而应贯穿于前端开发的整个生命周期。本文将从四个核心层面------代码、构建、渲染、网络------构建一套完整的性能优化体系。

一、核心性能指标(Core Web Vitals)

Largest Contentful Paint (LCP)

衡量页面主要内容加载完成的时间,理想值应小于2.5秒。优化方向包括压缩图片、延迟加载非关键资源、使用CDN加速静态资源。

First Input Delay (FID)

评估用户首次交互(如点击按钮)到浏览器响应的延迟时间,目标值需低于100毫秒。优化策略包括拆分长任务、减少主线程阻塞、使用Web Worker。

Cumulative Layout Shift (CLS)

量化页面布局稳定性,得分应低于0.1。避免动态插入内容导致偏移,为媒体元素预设尺寸,使用CSS transform动画替代影响布局的属性。

二、资源加载指标

Time to First Byte (TTFB)

服务器响应首字节的时间,建议控制在200ms内。可通过优化后端查询、启用缓存、升级服务器硬件实现。

Total Blocking Time (TBT)

主线程被长任务阻塞的总时间,影响交互响应。需拆分JavaScript任务、移除未使用的代码、优化事件监听器。

三、渲染效率指标

Speed Index

反映页面内容可视化完成速度,数值越低越好。优化关键渲染路径、内联关键CSS、预加载关键资源可改善此指标。

Time to Interactive (TTI)

从导航开始到页面完全可交互的时间。减少第三方脚本、按需加载Polyfill、使用PRPL模式能有效提升TTI。

四、性能检测


五、代码层优化:从源头提升效率

5.1 算法与数据结构的选择

核心原则:选择适合场景的算法和数据结构。在JavaScript中,简单的算法选择差异可能导致性能的巨大差距。

代码示例对比:寻找数组中的重复项

javascript 复制代码
// 不好的做法:嵌套循环 O(n²) 时间复杂度
// 当数组长度为1000时,需要执行近50万次比较
function findDuplicates(arr) {
  const duplicates = [];
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) {
        duplicates.push(arr[i]);
      }
    }
  }
  return duplicates;
}

// 好的做法:使用Set数据结构 O(n) 时间复杂度
// 利用Set的哈希表特性,只需遍历一次数组
function findDuplicatesOptimized(arr) {
  const seen = new Set();       // 存储已见过的元素
  const duplicates = new Set(); // 使用Set自动去重
  
  for (const item of arr) {
    if (seen.has(item)) {
      duplicates.add(item);    // 如果已经见过,就是重复项
    } else {
      seen.add(item);          // 第一次见到,加入已见集合
    }
  }
  return Array.from(duplicates); // 将Set转换回数组
}

5.2 避免不必要的计算和操作

防抖与节流:处理高频事件的利器

在用户交互中,scrollresizeinput等事件可能以极高的频率触发。如果不加限制,会导致大量不必要的函数调用和计算。

javascript 复制代码
// 防抖 (Debounce):事件触发后等待一段时间再执行
// 适用于搜索框输入、窗口调整大小等场景
function debounce(func, wait) {
  let timeout;  // 存储定时器ID
  
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);  // 清除之前的定时器
      func(...args);          // 执行目标函数
    };
    
    clearTimeout(timeout);    // 每次调用都清除之前的定时器
    timeout = setTimeout(later, wait); // 设置新的定时器
  };
}

// 节流 (Throttle):确保函数在一定时间内只执行一次
// 适用于滚动事件、鼠标移动等连续触发场景
function throttle(func, limit) {
  let inThrottle = false;  // 节流状态标志
  
  return function(...args) {
    if (!inThrottle) {      // 如果不在节流状态
      func.apply(this, args); // 执行函数
      inThrottle = true;      // 进入节流状态
      
      // 一段时间后解除节流
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// 实际应用示例
window.addEventListener('resize', debounce(handleResize, 250));
window.addEventListener('scroll', throttle(handleScroll, 100));
计算缓存(Memoization):避免重复计算

对于耗时的纯函数,可以通过缓存计算结果来提升性能。

javascript 复制代码
// Memoization 高阶函数
const memoize = (fn) => {
  const cache = new Map();  // 使用Map存储缓存结果
  
  return (...args) => {
    const key = JSON.stringify(args);  // 将参数序列化为缓存键
    
    if (cache.has(key)) {
      console.log('从缓存读取结果');
      return cache.get(key);  // 缓存命中,直接返回
    }
    
    console.log('执行计算并缓存结果');
    const result = fn(...args);  // 执行实际计算
    cache.set(key, result);      // 缓存结果
    return result;
  };
};

// 示例:优化斐波那契数列计算
// 原始递归实现时间复杂度为 O(2^n),使用缓存后降为 O(n)
const fibonacci = memoize((n) => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
});

console.log(fibonacci(10)); // 第一次计算,执行55次函数调用
console.log(fibonacci(10)); // 第二次直接从缓存读取

5.3 减少重绘与重排

关键理解

  • 重排(Reflow):改变元素的几何属性(尺寸、位置),需要重新计算布局
  • 重绘(Repaint):改变元素的外观(颜色、背景),不需要重新布局,但仍需绘制

浏览器渲染流程:JavaScript → 样式计算 → 布局 → 绘制 → 合成

优化策略对比

javascript 复制代码
// ❌ 不好的做法:多次修改样式,触发多次重排
// 每次修改style都会立即触发重排,性能极差
element.style.width = '100px';    // 触发重排
element.style.height = '200px';   // 触发重排
element.style.margin = '10px';    // 触发重排

// ✅ 好的做法1:使用classList批量修改
// 所有样式变化在一次重排中完成
element.classList.add('new-style');

// ✅ 好的做法2:使用cssText或直接修改className
element.style.cssText = 'width: 100px; height: 200px; margin: 10px;';

// ✅ 好的做法3:脱离文档流进行大量DOM操作
// 先隐藏元素,操作完成后再显示
element.style.display = 'none';       // 触发一次重排
// ... 进行大量DOM操作(此时不会触发重排)
element.style.display = 'block';      // 触发一次重排

// ✅ 好的做法4:使用文档片段批量插入
// DocumentFragment在内存中操作,不会触发DOM更新
const fragment = document.createDocumentFragment();

for (let i = 0; i < 100; i++) {
  const item = document.createElement('div');
  item.textContent = `Item ${i}`;
  fragment.appendChild(item);  // 内存操作,无重排
}

container.appendChild(fragment);  // 一次插入,触发一次重排

5.4 事件委托与懒加载

事件委托:减少事件监听器数量
javascript 复制代码
// ❌ 传统做法:为每个列表项绑定点击事件
// 如果有1000个列表项,就会创建1000个事件监听器
const listItems = document.querySelectorAll('#list li');
listItems.forEach(item => {
  item.addEventListener('click', () => {
    console.log('Clicked:', item.textContent);
  });
});

// ✅ 事件委托:利用事件冒泡机制
// 只在父元素上绑定一个事件监听器
document.getElementById('list').addEventListener('click', function(e) {
  // 检查点击的是否是列表项
  if (e.target.tagName === 'LI') {
    console.log('List item clicked:', e.target.textContent);
  }
  
  // 如果列表项内有其他元素,可以使用closest
  const listItem = e.target.closest('li');
  if (listItem) {
    console.log('Clicked item:', listItem.dataset.id);
  }
});
图片懒加载:按需加载非可视区域图片
javascript 复制代码
// 原生实现:Intersection Observer API
// 更高效,不依赖scroll事件,性能更好
const lazyImages = document.querySelectorAll('img[data-src]');

// 创建观察器实例
const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {  // 元素进入视口
      const img = entry.target;
      
      // 替换src属性,开始加载图片
      img.src = img.dataset.src;
      
      // 加载完成后移除data-src属性
      img.addEventListener('load', () => {
        img.removeAttribute('data-src');
      });
      
      // 停止观察该图片
      imageObserver.unobserve(img);
    }
  });
}, {
  rootMargin: '50px',    // 提前50px开始加载
  threshold: 0.1         // 10%可见时触发
});

// 开始观察所有懒加载图片
lazyImages.forEach(img => imageObserver.observe(img));

// HTML中使用示例
// <img data-src="large-image.jpg" src="placeholder.jpg" alt="描述">

六、构建层优化:高效的打包与交付

6.1 现代构建工具的选择与配置

不同的构建工具有不同的适用场景:

javascript 复制代码
// Vite 配置示例 (vite.config.js)
// Vite 使用ES模块原生支持,开发环境极速启动
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  build: {
    rollupOptions: {
      output: {
        // 代码分割策略
        manualChunks: {
          vendor: ['react', 'react-dom'],  // 第三方库
          utils: ['lodash', 'moment'],     // 工具库
        }
      }
    }
  }
});

// Webpack 配置示例 (webpack.config.js)
// Webpack 功能全面,适合复杂项目
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: '[name].[contenthash:8].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,  // 构建前清理dist目录
  },
  // ... 其他配置
};

6.2 代码分割与动态导入

代码分割是减少初始加载包大小的关键技术:

javascript 复制代码
// React 路由懒加载 + 错误边界 + 加载状态
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

// 错误边界组件
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  render() {
    if (this.state.hasError) {
      return <h1>页面加载失败</h1>;
    }
    return this.props.children;
  }
}

// 使用React.lazy进行动态导入
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const UserProfile = lazy(() => import('./pages/UserProfile'));

// 预加载策略:鼠标悬停时预加载
const preloadComponent = (importFunc) => {
  const component = lazy(() => importFunc());
  component.preload = importFunc;  // 添加预加载方法
  return component;
};

function App() {
  return (
    <Router>
      <ErrorBoundary>
        <Suspense 
          fallback={
            <div className="loading-spinner">
              页面加载中...
            </div>
          }
        >
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
            <Route path="/profile" component={UserProfile} />
          </Switch>
        </Suspense>
      </ErrorBoundary>
    </Router>
  );
}

6.3 Tree Shaking 与依赖优化

确保你的项目正确配置以支持Tree Shaking:

javascript 复制代码
// package.json 配置示例
{
  "name": "my-project",
  "sideEffects": [
    "*.css",      // 标记CSS文件有副作用
    "*.scss",
    "*.less",
    "polyfill.js" // 标记polyfill文件
  ],
  "module": "dist/esm/index.js",   // ES模块入口
  "main": "dist/cjs/index.js",     // CommonJS入口
  "exports": {
    ".": {
      "import": "./dist/esm/index.js",  // ES模块
      "require": "./dist/cjs/index.js"  // CommonJS
    }
  }
}

// 组件库按需引入对比
// ❌ 全部引入:打包所有组件,体积大
import * as Antd from 'antd';
import 'antd/dist/antd.css';

// ✅ 按需引入:只打包使用的组件
import { Button, Input, Form } from 'antd';
import Button from 'antd/es/button';       // 直接导入ES模块
import 'antd/es/button/style/css';         // 按需引入样式

// 使用babel-plugin-import自动转换(.babelrc)
{
  "plugins": [
    ["import", {
      "libraryName": "antd",
      "libraryDirectory": "es",
      "style": "css"  // 自动引入样式
    }]
  ]
}

6.4 资源压缩与优化配置

完整的Webpack优化配置示例:

javascript 复制代码
// webpack.prod.config.js
const path = require('path');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  mode: 'production',
  
  // 优化配置
  optimization: {
    minimize: true,  // 开启压缩
    
    minimizer: [
      // JavaScript压缩
      new TerserPlugin({
        parallel: true,  // 开启多进程并行压缩
        terserOptions: {
          compress: {
            drop_console: process.env.NODE_ENV === 'production', // 生产环境移除console
            drop_debugger: true,  // 移除debugger
            pure_funcs: ['console.log'],  // 移除console.log
          },
          mangle: true,  // 混淆变量名
          output: {
            comments: false,  // 移除注释
          },
        },
        extractComments: false,  // 不提取注释到单独文件
      }),
      
      // CSS压缩
      new CssMinimizerPlugin({
        minimizerOptions: {
          preset: [
            'default',
            {
              discardComments: { removeAll: true },  // 移除所有注释
            },
          ],
        },
      }),
    ],
    
    // 代码分割配置
    splitChunks: {
      chunks: 'all',  // 优化所有类型的chunks
      minSize: 20000,   // 最小大小20KB
      maxSize: 244000,  // 最大大小244KB,尝试分割更大的chunk
      minChunks: 1,     // 至少被引用一次
      maxAsyncRequests: 30,  // 最大异步请求数
      maxInitialRequests: 30, // 最大初始请求数
      automaticNameDelimiter: '~',  // 名称分隔符
      
      cacheGroups: {
        // 第三方库
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,  // 优先级
          name: 'vendors',
          reuseExistingChunk: true,
        },
        // 公共模块
        commons: {
          name: 'commons',
          minChunks: 2,  // 至少被2个chunk引用
          priority: -20,
          reuseExistingChunk: true,
        },
        // 按需加载的公共组件
        asyncCommons: {
          name: 'async-commons',
          minChunks: 2,
          chunks: 'async',  // 只处理异步chunk
          priority: -30,
          reuseExistingChunk: true,
        },
      },
    },
    
    // 运行时chunk
    runtimeChunk: {
      name: 'runtime',
    },
  },
  
  // 输出配置
  output: {
    filename: '[name].[contenthash:8].js',  // 使用内容哈希
    chunkFilename: '[name].[contenthash:8].chunk.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',  // CDN路径可以在这里配置
  },
  
  // 插件配置
  plugins: [
    // 提取CSS到单独文件
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:8].css',
      chunkFilename: '[name].[contenthash:8].chunk.css',
    }),
    
    // Gzip压缩
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,  // 只处理大于10KB的文件
      minRatio: 0.8,     // 压缩比小于0.8才处理
    }),
    
    // Brotli压缩(更高效的压缩算法)
    new CompressionPlugin({
      filename: '[path][base].br',
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
      compressionOptions: { level: 11 },  // 压缩级别1-11
      threshold: 10240,
      minRatio: 0.8,
    }),
    
    // 打包分析(开发时使用)
    process.env.ANALYZE && new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: 'bundle-report.html',
      openAnalyzer: false,
    }),
  ].filter(Boolean),  // 过滤掉false的插件
  
  // 模块配置
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|webp|avif)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024,  // 8KB以下的图片转base64
          },
        },
        generator: {
          filename: 'images/[name].[hash:8][ext]',
        },
      },
    ],
  },
  
  // 解析配置
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),  // 路径别名
    },
    extensions: ['.js', '.jsx', '.ts', '.tsx'],  // 自动解析的扩展名
  },
};
图片优化策略实践
html 复制代码
<!-- 使用现代图片格式和响应式图片 -->
<!-- picture元素提供多种格式支持,浏览器选择最优格式 -->
<picture>
  <!-- 优先使用AVIF格式(压缩率最高) -->
  <source 
    srcset="image.avif" 
    type="image/avif"
    media="(min-width: 1200px)"
  >
  <!-- 回退到WebP格式(兼容性更好) -->
  <source 
    srcset="image.webp" 
    type="image/webp"
    media="(min-width: 768px)"
  >
  <!-- 最终回退到JPEG -->
  <img 
    src="image.jpg" 
    alt="描述性文字"
    loading="lazy"  <!-- 原生懒加载 -->
    decoding="async" <!-- 异步解码 -->
    width="800"      <!-- 指定尺寸帮助布局计算 -->
    height="600"
  >
</picture>

<!-- 响应式图片:根据屏幕尺寸加载不同大小的图片 -->
<img 
  src="image-small.jpg"
  srcset="
    image-small.jpg 480w,
    image-medium.jpg 768w,
    image-large.jpg 1200w
  "
  sizes="
    (max-width: 480px) 100vw,
    (max-width: 768px) 80vw,
    1200px
  "
  alt="响应式图片示例"
>

七、渲染层优化:流畅的视觉体验

7.1 关键渲染路径优化实践

关键渲染路径(Critical Rendering Path)是从接收HTML、CSS、JavaScript到渲染像素的过程。优化CRP可以显著提升首屏加载速度。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <!-- 1. 关键CSS内联或优先加载 -->
  <!-- 首屏关键样式内联,避免阻塞渲染 -->
  <style>
    /* 首屏必要的最小样式 */
    .header, .hero, .navigation { display: block; }
    body { margin: 0; font-family: sans-serif; }
  </style>
  
  <!-- 2. 预加载关键资源 -->
  <!-- 字体预加载 -->
  <link 
    rel="preload" 
    href="font.woff2" 
    as="font" 
    type="font/woff2" 
    crossorigin
  >
  
  <!-- 首屏关键图片预加载 -->
  <link 
    rel="preload" 
    href="hero-image.webp" 
    as="image"
    imagesrcset="hero-image-small.webp 480w, hero-image-large.webp 1200w"
    imagesizes="100vw"
  >
  
  <!-- 3. 异步加载非关键CSS -->
  <link 
    rel="stylesheet" 
    href="non-critical.css" 
    media="print" 
    onload="this.media='all'"
  >
  
  <!-- 4. DNS预连接 -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://cdn.example.com">
  
  <title>性能优化示例</title>
</head>
<body>
  <!-- 首屏内容 -->
  <header class="header">...</header>
  <main class="main-content">
    <!-- 首屏可见内容优先 -->
    <section class="hero">...</section>
  </main>
  
  <!-- 非首屏内容延迟加载 -->
  <section class="secondary-content" data-lazy>
    <!-- 内容会在进入视口时加载 -->
  </section>
  
  <!-- 5. 脚本优化 -->
  <!-- 关键脚本内联或使用preload -->
  <script>
    // 关键功能脚本内联
    function initCriticalFeatures() {
      // 初始化首屏必要功能
    }
    document.addEventListener('DOMContentLoaded', initCriticalFeatures);
  </script>
  
  <!-- 非关键脚本使用defer -->
  <script src="analytics.js" defer></script>
  
  <!-- 第三方脚本使用async -->
  <script 
    src="https://third-party.com/widget.js" 
    async 
    crossorigin="anonymous"
  ></script>
  
  <!-- 延迟加载的脚本 -->
  <script>
    // 使用requestIdleCallback延迟非关键任务
    if ('requestIdleCallback' in window) {
      requestIdleCallback(() => {
        const script = document.createElement('script');
        script.src = 'non-critical.js';
        document.body.appendChild(script);
      });
    }
  </script>
</body>
</html>

7.2 CSS性能优化详解

CSS选择器的性能虽然在现代浏览器中影响较小,但在复杂页面中仍值得优化。

css 复制代码
/* ❌ 避免的选择器模式 */

/* 1. 过于具体的嵌套选择器 */
.container .wrapper .content .list .item .title span {
  color: red;
  /* 浏览器从右向左匹配:先找到所有span,再向上匹配 */
}

/* 2. 通配符过度使用 */
div * {
  margin: 0;
  /* 匹配所有div下的所有元素 */
}

/* 3. 属性选择器模糊匹配 */
a[href*="example"] {
  color: blue;
  /* 需要检查所有a标签的href属性 */
}

/* ✅ 推荐的优化实践 */

/* 1. 使用类选择器,减少嵌套 */
.item-title {
  color: red;
  font-size: 1.2em;
}

/* 2. 使用BEM命名规范,避免嵌套 */
.block__element--modifier {
  /* 独立样式,无需依赖父元素 */
}

/* 3. 减少不必要的层叠 */
.btn-primary {
  /* 直接定义,不依赖.btn */
  background: #007bff;
  color: white;
}

/* ✅ GPU加速动画优化 */
.animate-element {
  /* 触发GPU硬件加速的属性 */
  transform: translateZ(0);
  will-change: transform;  /* 提前告知浏览器可能的变化 */
  
  /* 使用transform和opacity做动画(不触发重排) */
  transition: 
    transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
    opacity 0.3s ease;
}

.animate-element:hover {
  transform: scale(1.05) translateY(-2px);
  opacity: 0.9;
}

/* 避免使用left/top做动画(触发重排) */
@keyframes slide {
  from { left: 0; }     /* ❌ 触发重排 */
  to { left: 100px; }
}

@keyframes slide-optimized {
  from { transform: translateX(0); }    /* ✅ GPU加速 */
  to { transform: translateX(100px); }
}

/* ✅ 使用CSS Containment API优化 */
.widget {
  /* 告诉浏览器这个元素的布局是独立的 */
  contain: layout style paint;
  
  /* 或更精细的控制 */
  contain-intrinsic-size: 300px 200px;  /* 预估尺寸 */
  content-visibility: auto;             /* 自动跳过不可见内容渲染 */
}

/* ✅ 减少重绘区域 */
.button {
  position: relative;
}

.button::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.1);
  opacity: 0;
  transition: opacity 0.3s;
}

.button:hover::before {
  opacity: 1;
  /* 只重绘伪元素,不影响按钮文本 */
}

/* ✅ 字体加载优化 */
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2'),
       url('font.woff') format('woff');
  font-display: swap;  /* 显示回退字体,字体加载后替换 */
  font-weight: 400;
  font-style: normal;
}

/* ✅ 使用CSS变量减少重复计算 */
:root {
  --primary-color: #007bff;
  --spacing-unit: 8px;
  --border-radius: 4px;
}

.component {
  color: var(--primary-color);
  margin: calc(var(--spacing-unit) * 2);
  border-radius: var(--border-radius);
}

7.3 JavaScript执行与动画优化

requestAnimationFrame优化动画
javascript 复制代码
// ❌ 使用setTimeout/setInterval的动画可能卡顿
function animateWithTimeout() {
  const element = document.getElementById('animated');
  let position = 0;
  
  function step() {
    position += 5;
    element.style.left = position + 'px';  // 触发重排
    
    if (position < 500) {
      setTimeout(step, 16);  // 约60fps
    }
  }
  
  step();
}

// ✅ 使用requestAnimationFrame优化动画
// 与浏览器刷新率同步,避免卡顿和过度绘制
class OptimizedAnimation {
  constructor(element) {
    this.element = element;
    this.position = 0;
    this.speed = 5;
    this.running = false;
    this.animationId = null;
  }
  
  start() {
    if (this.running) return;
    
    this.running = true;
    this.animate();
  }
  
  stop() {
    this.running = false;
    
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }
  }
  
  animate() {
    // 检查是否应该继续动画
    if (!this.running || this.position >= 500) {
      this.stop();
      return;
    }
    
    // 更新位置 - 使用transform避免重排
    this.position += this.speed;
    this.element.style.transform = `translateX(${this.position}px)`;
    
    // 请求下一帧
    this.animationId = requestAnimationFrame(() => this.animate());
  }
  
  // 获取当前FPS(性能监控)
  getFPS() {
    let lastTime = performance.now();
    let frameCount = 0;
    
    const checkFPS = (currentTime) => {
      frameCount++;
      
      if (currentTime - lastTime >= 1000) {
        const fps = Math.round((frameCount * 1000) / (currentTime - lastTime));
        console.log(`当前FPS: ${fps}`);
        
        frameCount = 0;
        lastTime = currentTime;
      }
      
      requestAnimationFrame(checkFPS);
    };
    
    requestAnimationFrame(checkFPS);
  }
}

// 使用示例
const animatedElement = document.getElementById('animated');
const animation = new OptimizedAnimation(animatedElement);
animation.start();

// 监听页面可见性,页面隐藏时暂停动画
document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    animation.stop();
  } else {
    animation.start();
  }
});
Web Workers处理耗时任务
javascript 复制代码
// main.js - 主线程
class TaskManager {
  constructor() {
    // 创建Web Worker
    this.worker = new Worker('worker.js');
    this.taskQueue = new Map();  // 任务队列
    this.taskId = 0;
    
    this.setupWorkerListeners();
  }
  
  setupWorkerListeners() {
    // 处理Worker返回的结果
    this.worker.onmessage = (event) => {
      const { taskId, result, error } = event.data;
      
      const task = this.taskQueue.get(taskId);
      if (task) {
        if (error) {
          task.reject(new Error(error));
        } else {
          task.resolve(result);
        }
        
        this.taskQueue.delete(taskId);
      }
    };
    
    // 处理Worker错误
    this.worker.onerror = (error) => {
      console.error('Worker error:', error);
      
      // 清理所有待处理任务
      for (const [taskId, task] of this.taskQueue) {
        task.reject(error);
        this.taskQueue.delete(taskId);
      }
    };
  }
  
  // 执行耗时任务
  executeTask(data, transferable = []) {
    return new Promise((resolve, reject) => {
      const taskId = ++this.taskId;
      
      // 存储任务引用
      this.taskQueue.set(taskId, { resolve, reject });
      
      // 发送任务到Worker
      this.worker.postMessage(
        { taskId, data },
        transferable.length > 0 ? transferable : undefined
      );
    });
  }
  
  // 批量处理大量数据
  async processLargeDataset(dataset) {
    const chunkSize = 1000;
    const chunks = [];
    
    // 分割数据
    for (let i = 0; i < dataset.length; i += chunkSize) {
      chunks.push(dataset.slice(i, i + chunkSize));
    }
    
    // 并行处理所有块
    const results = await Promise.all(
      chunks.map(chunk => this.executeTask(chunk))
    );
    
    // 合并结果
    return results.flat();
  }
  
  // 清理Worker
  terminate() {
    this.worker.terminate();
    this.taskQueue.clear();
  }
}

// 使用示例
const taskManager = new TaskManager();

// 处理图像数据
async function processImage(imageData) {
  try {
    // 转换ImageData为Transferable
    const { data } = imageData;
    const result = await taskManager.executeTask(
      { type: 'image', data: data.buffer },
      [data.buffer]  // 转移所有权,避免复制
    );
    
    return new ImageData(
      new Uint8ClampedArray(result.data),
      result.width,
      result.height
    );
  } catch (error) {
    console.error('图像处理失败:', error);
    throw error;
  }
}

// worker.js - Worker线程
// 处理耗时计算,不阻塞主线程
self.onmessage = async function(event) {
  const { taskId, data } = event.data;
  
  try {
    let result;
    
    // 根据任务类型处理
    if (data.type === 'image') {
      result = await processImageData(data);
    } else if (data.type === 'calculation') {
      result = heavyCalculation(data);
    } else {
      throw new Error(`未知任务类型: ${data.type}`);
    }
    
    // 返回结果给主线程
    self.postMessage({ taskId, result });
  } catch (error) {
    // 捕获错误并返回
    self.postMessage({ 
      taskId, 
      error: error.message 
    });
  }
};

// Worker中的图像处理函数
async function processImageData(data) {
  // 这里可以执行各种耗时操作
  // 例如:图像滤镜、人脸识别、数据转换等
  
  const buffer = data.data;
  const array = new Uint8ClampedArray(buffer);
  
  // 示例:简单的灰度处理
  for (let i = 0; i < array.length; i += 4) {
    const avg = (array[i] + array[i + 1] + array[i + 2]) / 3;
    array[i] = avg;     // R
    array[i + 1] = avg; // G
    array[i + 2] = avg; // B
    // Alpha通道保持不变
  }
  
  return {
    data: array.buffer,
    width: data.width || 0,
    height: data.height || 0
  };
}

function heavyCalculation(data) {
  // 模拟复杂计算
  let result = 0;
  for (let i = 0; i < 1000000; i++) {
    result += Math.sin(i) * Math.cos(i);
  }
  return { result };
}

7.4 虚拟列表优化长列表渲染

虚拟列表是处理大量数据展示的关键技术,它只渲染可视区域内的元素。

javascript 复制代码
/**
 * 高性能虚拟列表组件
 * 支持动态高度、滚动缓冲、异步加载
 */
class VirtualList {
  constructor(container, config = {}) {
    // 容器元素
    this.container = container;
    
    // 配置项
    this.config = {
      itemHeight: 50,           // 预估项目高度
      bufferCount: 5,           // 缓冲区项目数
      overscanCount: 3,         // 前后预加载项目数
      throttleDelay: 16,        // 节流延迟
      ...config
    };
    
    // 状态
    this.items = [];            // 所有数据项
    this.visibleItems = [];     // 当前可见项目
    this.startIndex = 0;        // 起始索引
    this.endIndex = 0;          // 结束索引
    this.totalHeight = 0;       // 列表总高度
    this.scrollTop = 0;         // 当前滚动位置
    this.isScrolling = false;   // 是否正在滚动
    
    // 渲染相关
    this.contentEl = null;      // 内容容器
    this.placeholderEl = null;  // 占位元素
    
    // 性能监控
    this.renderTimes = [];
    this.lastRenderTime = 0;
    
    this.init();
  }
  
  init() {
    this.setupDOM();
    this.setupEvents();
    this.calculateMetrics();
  }
  
  setupDOM() {
    // 创建内部结构
    this.container.style.position = 'relative';
    this.container.style.overflow = 'auto';
    
    // 占位元素 - 撑开滚动区域
    this.placeholderEl = document.createElement('div');
    this.placeholderEl.style.position = 'absolute';
    this.placeholderEl.style.top = '0';
    this.placeholderEl.style.left = '0';
    this.placeholderEl.style.width = '100%';
    this.container.appendChild(this.placeholderEl);
    
    // 内容容器 - 实际渲染内容
    this.contentEl = document.createElement('div');
    this.contentEl.style.position = 'absolute';
    this.contentEl.style.top = '0';
    this.contentEl.style.left = '0';
    this.contentEl.style.width = '100%';
    this.container.appendChild(this.contentEl);
  }
  
  setupEvents() {
    // 使用节流处理滚动事件
    this.handleScroll = this.throttle(() => {
      this.onScroll();
    }, this.config.throttleDelay);
    
    this.container.addEventListener('scroll', this.handleScroll);
    
    // 监听窗口大小变化
    this.handleResize = this.throttle(() => {
      this.calculateMetrics();
      this.renderVisibleItems();
    }, 100);
    
    window.addEventListener('resize', this.handleResize);
    
    // 使用Intersection Observer监听可见性
    if ('IntersectionObserver' in window) {
      this.visibilityObserver = new IntersectionObserver(
        (entries) => {
          entries.forEach(entry => {
            if (entry.isIntersecting) {
              this.onContainerVisible();
            } else {
              this.onContainerHidden();
            }
          });
        },
        { threshold: 0.1 }
      );
      
      this.visibilityObserver.observe(this.container);
    }
  }
  
  // 设置数据
  setItems(items) {
    this.items = items;
    this.calculateTotalHeight();
    this.calculateVisibleRange();
    this.renderVisibleItems();
  }
  
  // 计算总高度
  calculateTotalHeight() {
    if (Array.isArray(this.config.itemHeight)) {
      // 动态高度
      this.totalHeight = this.items.reduce((sum, item, index) => {
        return sum + (this.config.itemHeight[index] || this.config.itemHeight.default || 50);
      }, 0);
    } else {
      // 固定高度
      this.totalHeight = this.items.length * this.config.itemHeight;
    }
    
    this.placeholderEl.style.height = `${this.totalHeight}px`;
  }
  
  // 计算可见范围
  calculateVisibleRange() {
    const { itemHeight, bufferCount } = this.config;
    const containerHeight = this.container.clientHeight;
    
    // 计算理论上应该渲染的项目数
    const visibleCount = Math.ceil(containerHeight / itemHeight) + bufferCount * 2;
    
    // 计算起始索引
    let newStartIndex = Math.floor(this.scrollTop / itemHeight) - bufferCount;
    newStartIndex = Math.max(0, newStartIndex);
    
    // 计算结束索引
    let newEndIndex = newStartIndex + visibleCount;
    newEndIndex = Math.min(this.items.length - 1, newEndIndex);
    
    // 添加前后预加载
    newStartIndex = Math.max(0, newStartIndex - this.config.overscanCount);
    newEndIndex = Math.min(this.items.length - 1, newEndIndex + this.config.overscanCount);
    
    this.startIndex = newStartIndex;
    this.endIndex = newEndIndex;
  }
  
  // 渲染可见项目
  renderVisibleItems() {
    const startTime = performance.now();
    
    // 计算需要渲染的项目
    const itemsToRender = this.items.slice(this.startIndex, this.endIndex + 1);
    
    // 批量DOM操作
    const fragment = document.createDocumentFragment();
    
    itemsToRender.forEach((item, relativeIndex) => {
      const absoluteIndex = this.startIndex + relativeIndex;
      const itemElement = this.renderItem(item, absoluteIndex);
      
      // 设置位置
      let top = 0;
      if (Array.isArray(this.config.itemHeight)) {
        // 动态高度计算
        for (let i = 0; i < absoluteIndex; i++) {
          top += this.config.itemHeight[i] || this.config.itemHeight.default || 50;
        }
      } else {
        // 固定高度计算
        top = absoluteIndex * this.config.itemHeight;
      }
      
      itemElement.style.position = 'absolute';
      itemElement.style.top = `${top}px`;
      itemElement.style.width = '100%';
      
      fragment.appendChild(itemElement);
    });
    
    // 清空并添加新元素
    this.contentEl.innerHTML = '';
    this.contentEl.appendChild(fragment);
    
    // 更新可见项目列表
    this.visibleItems = itemsToRender;
    
    // 性能记录
    const renderTime = performance.now() - startTime;
    this.renderTimes.push(renderTime);
    
    if (this.renderTimes.length > 60) {
      this.renderTimes.shift();
    }
    
    // 记录最后渲染时间
    this.lastRenderTime = Date.now();
    
    // 如果渲染时间过长,发出警告
    if (renderTime > 16) { // 超过一帧时间
      console.warn(`虚拟列表渲染耗时: ${renderTime.toFixed(2)}ms`);
    }
  }
  
  // 渲染单个项目(可被子类覆盖)
  renderItem(item, index) {
    const element = document.createElement('div');
    element.className = 'virtual-list-item';
    element.textContent = `Item ${index}: ${item}`;
    element.dataset.index = index;
    
    // 添加数据属性便于调试
    element.dataset.originalIndex = index;
    element.dataset.item = JSON.stringify(item);
    
    return element;
  }
  
  // 滚动事件处理
  onScroll() {
    this.scrollTop = this.container.scrollTop;
    this.isScrolling = true;
    
    // 清除之前的定时器
    if (this.scrollEndTimer) {
      clearTimeout(this.scrollEndTimer);
    }
    
    // 计算新的可见范围
    const oldStartIndex = this.startIndex;
    const oldEndIndex = this.endIndex;
    
    this.calculateVisibleRange();
    
    // 如果可见范围变化超过阈值,重新渲染
    const threshold = this.config.bufferCount;
    if (
      Math.abs(this.startIndex - oldStartIndex) > threshold ||
      Math.abs(this.endIndex - oldEndIndex) > threshold
    ) {
      this.renderVisibleItems();
    }
    
    // 设置滚动结束检测
    this.scrollEndTimer = setTimeout(() => {
      this.isScrolling = false;
      this.onScrollEnd();
    }, 150);
  }
  
  // 滚动结束
  onScrollEnd() {
    // 滚动结束后进行一次精确渲染
    this.calculateVisibleRange();
    this.renderVisibleItems();
    
    // 触发滚动结束事件
    this.triggerEvent('scrollend');
  }
  
  // 容器可见时
  onContainerVisible() {
    // 恢复渲染
    this.isContainerVisible = true;
    this.renderVisibleItems();
  }
  
  // 容器隐藏时
  onContainerHidden() {
    // 暂停渲染以节省性能
    this.isContainerVisible = false;
    this.contentEl.innerHTML = '';
  }
  
  // 节流函数
  throttle(func, delay) {
    let lastCall = 0;
    let timeout = null;
    
    return (...args) => {
      const now = Date.now();
      const remaining = delay - (now - lastCall);
      
      if (remaining <= 0) {
        lastCall = now;
        func.apply(this, args);
      } else if (!timeout) {
        timeout = setTimeout(() => {
          lastCall = Date.now();
          timeout = null;
          func.apply(this, args);
        }, remaining);
      }
    };
  }
  
  // 事件触发器
  triggerEvent(eventName, detail = {}) {
    const event = new CustomEvent(eventName, {
      detail: {
        ...detail,
        instance: this
      }
    });
    
    this.container.dispatchEvent(event);
  }
  
  // 获取性能指标
  getPerformanceMetrics() {
    if (this.renderTimes.length === 0) return null;
    
    const sum = this.renderTimes.reduce((a, b) => a + b, 0);
    const avg = sum / this.renderTimes.length;
    
    return {
      averageRenderTime: avg,
      maxRenderTime: Math.max(...this.renderTimes),
      minRenderTime: Math.min(...this.renderTimes),
      renderCount: this.renderTimes.length,
      lastRenderTime: this.lastRenderTime
    };
  }
  
  // 清理资源
  destroy() {
    this.container.removeEventListener('scroll', this.handleScroll);
    window.removeEventListener('resize', this.handleResize);
    
    if (this.visibilityObserver) {
      this.visibilityObserver.disconnect();
    }
    
    if (this.scrollEndTimer) {
      clearTimeout(this.scrollEndTimer);
    }
    
    this.contentEl.innerHTML = '';
    this.placeholderEl.remove();
    this.contentEl.remove();
    
    this.items = [];
    this.visibleItems = [];
  }
}

// 使用示例
class AdvancedVirtualList extends VirtualList {
  constructor(container, config) {
    super(container, config);
    
    // 添加项高度缓存
    this.itemHeightCache = new Map();
    
    // 添加项渲染缓存
    this.itemRenderCache = new Map();
    
    // 添加图片懒加载
    this.imageObserver = null;
    this.setupImageLazyLoad();
  }
  
  setupImageLazyLoad() {
    if ('IntersectionObserver' in window) {
      this.imageObserver = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            img.classList.add('loaded');
            this.imageObserver.unobserve(img);
          }
        });
      }, {
        root: this.container,
        rootMargin: '50px',
        threshold: 0.1
      });
    }
  }
  
  renderItem(item, index) {
    // 检查缓存
    if (this.itemRenderCache.has(index)) {
      return this.itemRenderCache.get(index).cloneNode(true);
    }
    
    const element = document.createElement('div');
    element.className = 'virtual-list-item';
    element.dataset.index = index;
    
    // 复杂项目渲染
    element.innerHTML = `
      <div class="item-content">
        <div class="item-header">
          <h3>${item.title}</h3>
          <span class="item-date">${item.date}</span>
        </div>
        <div class="item-body">
          <p>${item.description}</p>
          ${item.image ? `
            <img 
              class="lazy-image" 
              data-src="${item.image}" 
              alt="${item.title}"
              width="100" 
              height="100"
            >
          ` : ''}
        </div>
        <div class="item-footer">
          <span class="item-tags">${item.tags?.join(', ') || ''}</span>
        </div>
      </div>
    `;
    
    // 缓存渲染结果
    this.itemRenderCache.set(index, element.cloneNode(true));
    
    // 设置懒加载图片
    const lazyImage = element.querySelector('.lazy-image');
    if (lazyImage && this.imageObserver) {
      this.imageObserver.observe(lazyImage);
    }
    
    return element;
  }
  
  // 测量并缓存项目高度
  measureItemHeight(element, index) {
    if (this.itemHeightCache.has(index)) {
      return this.itemHeightCache.get(index);
    }
    
    // 临时添加到文档测量
    const tempContainer = document.createElement('div');
    tempContainer.style.position = 'absolute';
    tempContainer.style.visibility = 'hidden';
    tempContainer.appendChild(element.cloneNode(true));
    document.body.appendChild(tempContainer);
    
    const height = element.offsetHeight;
    this.itemHeightCache.set(index, height);
    
    document.body.removeChild(tempContainer);
    
    return height;
  }
  
  destroy() {
    super.destroy();
    
    if (this.imageObserver) {
      this.imageObserver.disconnect();
    }
    
    this.itemHeightCache.clear();
    this.itemRenderCache.clear();
  }
}

// 初始化虚拟列表
document.addEventListener('DOMContentLoaded', () => {
  const container = document.getElementById('list-container');
  
  // 生成测试数据
  const mockData = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    title: `项目 ${i + 1}`,
    description: `这是第 ${i + 1} 个项目的详细描述内容...`,
    date: new Date().toLocaleDateString(),
    tags: ['标签1', '标签2', '标签3'].slice(0, Math.random() * 3 + 1),
    image: i % 5 === 0 ? `https://picsum.photos/100/100?random=${i}` : null
  }));
  
  // 创建虚拟列表实例
  const virtualList = new AdvancedVirtualList(container, {
    itemHeight: 100,  // 预估高度
    bufferCount: 10,
    overscanCount: 5
  });
  
  // 设置数据
  virtualList.setItems(mockData);
  
  // 监听事件
  container.addEventListener('scrollend', () => {
    console.log('滚动结束');
  });
  
  // 获取性能数据
  setInterval(() => {
    const metrics = virtualList.getPerformanceMetrics();
    if (metrics) {
      console.log('渲染性能:', metrics);
    }
  }, 5000);
  
  // 动态添加数据示例
  setTimeout(() => {
    const newData = Array.from({ length: 1000 }, (_, i) => ({
      id: 10000 + i,
      title: `新项目 ${i + 1}`,
      description: '新添加的数据...',
      date: new Date().toLocaleDateString(),
      tags: ['新标签'],
      image: null
    }));
    
    virtualList.setItems([...mockData, ...newData]);
  }, 10000);
});

八、网络层优化:极速的内容传输

8.1 HTTP缓存策略详解与配置

缓存是提升网页加载速度最有效的手段之一。正确的缓存策略可以减少服务器负载,提升用户体验。

Nginx缓存配置示例
nginx 复制代码
# nginx.conf - 完整的性能优化配置
http {
  # 基础优化
  sendfile on;                  # 使用sendfile系统调用
  tcp_nopush on;                # 在sendfile开启时优化数据包
  tcp_nodelay on;               # 禁用Nagle算法
  keepalive_timeout 65;         # 保持连接时间
  types_hash_max_size 2048;
  
  # MIME类型
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  
  # 日志格式
  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';
  
  # Gzip压缩配置
  gzip on;
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;           # 压缩级别1-9,6是平衡点
  gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/javascript
    application/x-javascript
    application/json
    application/xml
    application/xml+rss
    image/svg+xml;
  
  # Brotli压缩(需要安装ngx_brotli模块)
  # brotli on;
  # brotli_comp_level 6;
  # brotli_types text/plain text/css application/javascript application/json image/svg+xml;
  
  # 缓存路径配置
  proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g
                   inactive=60m use_temp_path=off;
  
  server {
    listen 80;
    server_name example.com;
    
    # 静态资源 - 强缓存策略
    location ~* \.(jpg|jpeg|png|gif|ico|webp|avif|svg|svgz)$ {
      # 图片资源缓存1年
      expires 1y;
      add_header Cache-Control "public, immutable";
      
      # 开启图片优化
      image_filter resize 800 600;  # 按需调整大小
      image_filter_jpeg_quality 85; # JPEG质量
      
      # 响应头优化
      add_header X-Content-Type-Options "nosniff";
      add_header X-Frame-Options "SAMEORIGIN";
      
      # 访问日志(可选关闭以提升性能)
      access_log off;
    }
    
    location ~* \.(css|js)$ {
      # CSS和JS资源缓存1年,使用内容哈希
      expires 1y;
      add_header Cache-Control "public, immutable";
      
      # 安全头
      add_header X-Content-Type-Options "nosniff";
      
      # 预压缩版本
      gzip_static on;  # 优先使用预压缩的.gz文件
    }
    
    location ~* \.(woff|woff2|ttf|eot)$ {
      # 字体文件缓存1年
      expires 1y;
      add_header Cache-Control "public, immutable";
      add_header Access-Control-Allow-Origin "*";  # 跨域字体
    }
    
    # HTML文件 - 协商缓存
    location ~* \.html$ {
      expires 1h;  # 短期缓存
      add_header Cache-Control "public, must-revalidate";
      etag on;     # 开启ETag
    }
    
    # API接口 - 精细缓存控制
    location /api/ {
      proxy_pass http://backend;
      proxy_cache my_cache;
      proxy_cache_key "$scheme$request_method$host$request_uri";
      proxy_cache_valid 200 5m;   # 成功响应缓存5分钟
      proxy_cache_valid 404 1m;   # 404响应缓存1分钟
      proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
      
      # 缓存控制头
      add_header X-Cache-Status $upstream_cache_status;
      
      # 连接优化
      proxy_connect_timeout 5s;
      proxy_send_timeout 10s;
      proxy_read_timeout 30s;
    }
    
    # 安全头配置
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # 根目录配置
    location / {
      root /usr/share/nginx/html;
      index index.html index.htm;
      
      # SPA应用路由支持
      try_files $uri $uri/ /index.html;
      
      # 安全头
      add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self' https://api.example.com;";
    }
    
    # 健康检查
    location /health {
      access_log off;
      return 200 "healthy\n";
      add_header Content-Type text/plain;
    }
  }
}
CDN缓存策略配置
javascript 复制代码
// Cloudflare Workers - 边缘计算缓存示例
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const url = new URL(request.url);
  
  // 只缓存GET请求
  if (request.method !== 'GET') {
    return fetch(request);
  }
  
  // 定义缓存策略
  const cacheRules = {
    '/api/data': { ttl: 300 },      // 5分钟
    '/api/user': { ttl: 60 },       // 1分钟
    '/static/': { ttl: 31536000 },  // 1年
    '/images/': { ttl: 86400 },     // 1天
    default: { ttl: 600 }           // 10分钟
  };
  
  // 匹配缓存规则
  let cacheTTL = cacheRules.default.ttl;
  for (const [path, rule] of Object.entries(cacheRules)) {
    if (url.pathname.startsWith(path)) {
      cacheTTL = rule.ttl;
      break;
    }
  }
  
  // 构建缓存键
  const cacheKey = new Request(url.toString(), request);
  const cache = caches.default;
  
  // 尝试从缓存读取
  let response = await cache.match(cacheKey);
  
  if (!response) {
    // 缓存未命中,从源站获取
    response = await fetch(request);
    
    // 只缓存成功的响应
    if (response.ok) {
      // 克隆响应以便缓存
      const responseToCache = response.clone();
      
      // 设置缓存头
      const headers = new Headers(responseToCache.headers);
      headers.set('Cache-Control', `public, max-age=${cacheTTL}`);
      headers.set('CDN-Cache-Control', `max-age=${cacheTTL}`);
      
      // 添加自定义头
      headers.set('X-Cache-Status', 'MISS');
      headers.set('X-Edge-Location', 'Cloudflare');
      
      // 存储到缓存
      event.waitUntil(
        cache.put(cacheKey, new Response(responseToCache.body, {
          status: responseToCache.status,
          statusText: responseToCache.statusText,
          headers: headers
        }))
      );
    }
  } else {
    // 缓存命中,更新缓存状态头
    const headers = new Headers(response.headers);
    headers.set('X-Cache-Status', 'HIT');
    response = new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers: headers
    });
  }
  
  // 添加Timing-Allow-Origin头支持性能监测
  response.headers.set('Timing-Allow-Origin', '*');
  
  return response;
}

// 缓存预热脚本
async function warmCache(urls) {
  const cache = caches.default;
  
  for (const url of urls) {
    try {
      const request = new Request(url);
      const response = await fetch(request);
      
      if (response.ok) {
        await cache.put(request, response.clone());
        console.log(`缓存预热成功: ${url}`);
      }
    } catch (error) {
      console.error(`缓存预热失败: ${url}`, error);
    }
  }
}

// 要预热的URL列表
const criticalURLs = [
  'https://example.com/',
  'https://example.com/static/main.css',
  'https://example.com/static/main.js',
  'https://example.com/api/initial-data'
];

// 定期执行缓存预热
setInterval(() => warmCache(criticalURLs), 5 * 60 * 1000); // 每5分钟

8.2 资源预加载与预连接策略

现代浏览器提供了多种资源提示(Resource Hints)来优化资源加载。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <!-- 1. DNS预解析 -->
  <!-- 提前解析第三方域名的DNS -->
  <link rel="dns-prefetch" href="https://fonts.googleapis.com">
  <link rel="dns-prefetch" href="https://cdn.example.com">
  <link rel="dns-prefetch" href="https://analytics.example.com">
  
  <!-- 2. 预连接 -->
  <!-- 提前建立TCP连接和TLS握手 -->
  <link 
    rel="preconnect" 
    href="https://fonts.googleapis.com" 
    crossorigin
  >
  <link 
    rel="preconnect" 
    href="https://fonts.gstatic.com" 
    crossorigin
  >
  <link 
    rel="preconnect" 
    href="https://api.example.com"
  >
  
  <!-- 3. 预加载关键资源 -->
  <!-- 首屏渲染必须的资源 -->
  <link 
    rel="preload" 
    href="/static/fonts/primary.woff2" 
    as="font" 
    type="font/woff2" 
    crossorigin
  >
  <link 
    rel="preload" 
    href="/static/css/critical.css" 
    as="style"
  >
  <link 
    rel="preload" 
    href="/static/js/main.js" 
    as="script"
  >
  
  <!-- 关键图片预加载 -->
  <link 
    rel="preload" 
    href="/static/images/hero.webp" 
    as="image"
    imagesrcset="
      /static/images/hero-480.webp 480w,
      /static/images/hero-768.webp 768w,
      /static/images/hero-1200.webp 1200w
    "
    imagesizes="100vw"
  >
  
  <!-- 4. 预获取 -->
  <!-- 提前加载下一页可能需要的资源 -->
  <link 
    rel="prefetch" 
    href="/about" 
    as="document"
  >
  <link 
    rel="prefetch" 
    href="/static/js/about-page.js" 
    as="script"
  >
  <link 
    rel="prefetch" 
    href="/static/css/about.css" 
    as="style"
  >
  
  <!-- 5. 预渲染 -->
  <!-- 在后台预先渲染整个页面(谨慎使用) -->
  <link 
    rel="prerender" 
    href="/next-page"
  >
  
  <!-- 6. 模块预加载 -->
  <!-- ES模块的预加载 -->
  <link 
    rel="modulepreload" 
    href="/static/js/vendor.js"
  >
  <link 
    rel="modulepreload" 
    href="/static/js/app.js"
  >
  
  <!-- 7. 内联关键CSS -->
  <style>
    /* 首屏关键样式 */
    :root {
      --primary-color: #007bff;
      --font-primary: 'PrimaryFont', sans-serif;
    }
    
    /* 字体加载期间的回退样式 */
    @font-face {
      font-family: 'PrimaryFont';
      src: local('Arial');
      font-display: swap;
    }
    
    /* 首屏布局 */
    .critical-content {
      opacity: 1;
      transition: opacity 0.3s;
    }
    
    /* 避免布局抖动 */
    .hero-image-container {
      position: relative;
      padding-bottom: 56.25%; /* 16:9宽高比 */
      overflow: hidden;
    }
    
    .hero-image {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  </style>
  
  <!-- 8. 异步加载非关键CSS -->
  <link 
    rel="stylesheet" 
    href="/static/css/non-critical.css" 
    media="print" 
    onload="this.media='all'; this.onload=null;"
  >
  <noscript>
    <link rel="stylesheet" href="/static/css/non-critical.css">
  </noscript>
  
  <title>资源预加载优化示例</title>
</head>
<body>
  <!-- 使用JavaScript动态控制资源加载 -->
  <script>
    // 资源加载管理器
    class ResourceLoader {
      constructor() {
        this.loaded = new Set();
        this.pending = new Map();
        this.priorityQueue = [];
      }
      
      // 根据网络状况调整加载策略
      adjustLoadingStrategy() {
        const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
        
        if (connection) {
          const { effectiveType, saveData } = connection;
          
          // 根据网络类型调整
          switch (effectiveType) {
            case 'slow-2g':
            case '2g':
              this.setLowBandwidthMode();
              break;
            case '3g':
              this.setMediumBandwidthMode();
              break;
            case '4g':
              this.setHighBandwidthMode();
              break;
          }
          
          // 省流模式
          if (saveData) {
            this.enableDataSaverMode();
          }
        }
      }
      
      setLowBandwidthMode() {
        // 低带宽模式:只加载关键资源
        console.log('低带宽模式:优化资源加载');
        
        // 延迟非关键资源
        this.deferNonCriticalResources();
        
        // 降低图片质量
        this.reduceImageQuality();
      }
      
      // 动态加载脚本
      loadScript(url, options = {}) {
        return new Promise((resolve, reject) => {
          if (this.loaded.has(url)) {
            resolve();
            return;
          }
          
          const script = document.createElement('script');
          script.src = url;
          
          // 设置属性
          if (options.async) script.async = true;
          if (options.defer) script.defer = true;
          if (options.crossOrigin) script.crossOrigin = options.crossOrigin;
          
          script.onload = () => {
            this.loaded.add(url);
            resolve();
          };
          
          script.onerror = reject;
          
          // 根据优先级插入
          if (options.priority === 'high') {
            document.head.prepend(script);
          } else {
            document.head.appendChild(script);
          }
        });
      }
      
      // 动态预加载资源
      prefetchResource(url, as = 'script') {
        if (this.loaded.has(url) || this.pending.has(url)) {
          return;
        }
        
        const link = document.createElement('link');
        link.rel = 'prefetch';
        link.href = url;
        link.as = as;
        
        this.pending.set(url, link);
        document.head.appendChild(link);
        
        // 监听加载完成
        link.onload = () => {
          this.loaded.add(url);
          this.pending.delete(url);
        };
      }
      
      // 预连接
      preconnectToOrigin(origin) {
        const link = document.createElement('link');
        link.rel = 'preconnect';
        link.href = origin;
        link.crossOrigin = true;
        document.head.appendChild(link);
      }
    }
    
    // 初始化资源加载器
    const resourceLoader = new ResourceLoader();
    
    // 页面加载完成后预加载下一页资源
    window.addEventListener('load', () => {
      // 预加载用户可能访问的下一页
      setTimeout(() => {
        resourceLoader.prefetchResource('/products', 'document');
        resourceLoader.prefetchResource('/static/js/products.js', 'script');
        resourceLoader.prefetchResource('/static/css/products.css', 'style');
      }, 1000);
      
      // 预连接API域名
      resourceLoader.preconnectToOrigin('https://api.example.com');
    });
    
    // 根据用户行为预测加载
    document.addEventListener('mouseover', (e) => {
      const link = e.target.closest('a');
      if (link && link.href) {
        const url = new URL(link.href);
        
        // 如果是站内链接,预加载
        if (url.origin === window.location.origin) {
          resourceLoader.prefetchResource(link.href, 'document');
        }
      }
    });
  </script>
  
  <!-- 主要内容 -->
  <div class="critical-content">
    <!-- 首屏内容 -->
  </div>
  
  <!-- 延迟加载的脚本 -->
  <script defer src="/static/js/non-critical.js"></script>
</body>
</html>
JavaScript动态资源管理
javascript 复制代码
// 高级资源管理器
class AdvancedResourceManager {
  constructor() {
    this.resources = new Map();
    this.observers = new Map();
    this.performanceEntries = [];
    
    this.initPerformanceMonitoring();
    this.initNetworkAwareLoading();
  }
  
  // 初始化性能监控
  initPerformanceMonitoring() {
    // 监听资源加载性能
    if ('PerformanceObserver' in window) {
      // 资源加载性能
      const resourceObserver = new PerformanceObserver((list) => {
        list.getEntries().forEach(entry => {
          this.performanceEntries.push(entry);
          this.analyzeResourcePerformance(entry);
        });
      });
      
      resourceObserver.observe({ entryTypes: ['resource'] });
      
      // 长任务监控
      const longTaskObserver = new PerformanceObserver((list) => {
        list.getEntries().forEach(entry => {
          if (entry.duration > 50) {
            console.warn('长任务影响交互:', entry);
          }
        });
      });
      
      longTaskObserver.observe({ entryTypes: ['longtask'] });
    }
  }
  
  // 网络感知加载
  initNetworkAwareLoading() {
    if ('connection' in navigator) {
      const connection = navigator.connection;
      
      connection.addEventListener('change', () => {
        this.adjustLoadingStrategy();
      });
      
      this.adjustLoadingStrategy();
    }
  }
  
  // 调整加载策略
  adjustLoadingStrategy() {
    const connection = navigator.connection;
    let strategy = 'normal';
    
    if (connection) {
      const { effectiveType, downlink, rtt, saveData } = connection;
      
      if (saveData) {
        strategy = 'save-data';
      } else if (effectiveType === 'slow-2g' || effectiveType === '2g') {
        strategy = 'low-bandwidth';
      } else if (effectiveType === '3g') {
        strategy = 'medium-bandwidth';
      } else {
        strategy = 'high-bandwidth';
      }
      
      console.log(`网络状况: ${effectiveType}, 下载速度: ${downlink}Mbps, 策略: ${strategy}`);
    }
    
    this.applyLoadingStrategy(strategy);
  }
  
  applyLoadingStrategy(strategy) {
    switch (strategy) {
      case 'save-data':
        this.enableDataSaverMode();
        break;
      case 'low-bandwidth':
        this.enableLowBandwidthMode();
        break;
      case 'medium-bandwidth':
        this.enableMediumBandwidthMode();
        break;
      case 'high-bandwidth':
        this.enableHighBandwidthMode();
        break;
    }
  }
  
  enableDataSaverMode() {
    // 省流模式:最小化资源加载
    console.log('启用省流模式');
    
    // 停止所有非关键资源加载
    this.stopNonCriticalLoading();
    
    // 降低图片质量
    this.reduceImageQuality(0.3);
    
    // 禁用动画
    this.disableAnimations();
    
    // 使用更小的字体
    this.useCompactLayout();
  }
  
  enableLowBandwidthMode() {
    // 低带宽优化
    console.log('启用低带宽模式');
    
    // 延迟非关键资源
    this.deferNonCriticalResources();
    
    // 降低图片质量
    this.reduceImageQuality(0.5);
    
    // 简化UI
    this.simplifyUI();
  }
  
  // 智能图片加载
  loadImage(src, options = {}) {
    return new Promise((resolve, reject) => {
      const img = new Image();
      
      // 设置加载属性
      img.loading = options.lazy ? 'lazy' : 'eager';
      img.decoding = options.async ? 'async' : 'sync';
      
      // 响应式图片支持
      if (options.srcset) {
        img.srcset = options.srcset;
      }
      if (options.sizes) {
        img.sizes = options.sizes;
      }
      
      // 设置基础src
      img.src = src;
      
      // 加载完成
      img.onload = () => {
        // 添加到资源管理
        this.resources.set(src, {
          type: 'image',
          loaded: true,
          timestamp: Date.now()
        });
        
        resolve(img);
      };
      
      img.onerror = reject;
      
      // 开始加载
      document.body.appendChild(img);
      img.style.display = 'none';
    });
  }
  
  // 分析资源性能
  analyzeResourcePerformance(entry) {
    const { name, duration, transferSize, encodedBodySize } = entry;
    
    // 识别性能问题
    if (duration > 1000) {
      console.warn(`资源加载过慢: ${name}`, entry);
      
      // 记录到分析服务
      this.reportPerformanceIssue({
        type: 'slow_resource',
        resource: name,
        duration,
        size: transferSize || encodedBodySize
      });
    }
    
    // 识别过大资源
    const size = transferSize || encodedBodySize;
    if (size > 1024 * 500) { // 大于500KB
      console.warn(`资源过大: ${name} (${(size / 1024).toFixed(2)}KB)`);
    }
  }
  
  // 报告性能问题
  reportPerformanceIssue(issue) {
    // 发送到监控服务
    if (navigator.sendBeacon) {
      const data = JSON.stringify({
        ...issue,
        url: window.location.href,
        timestamp: Date.now(),
        userAgent: navigator.userAgent
      });
      
      navigator.sendBeacon('/api/performance-log', data);
    }
  }
  
  // 清理未使用的资源
  cleanupUnusedResources() {
    const now = Date.now();
    const maxAge = 30 * 60 * 1000; // 30分钟
    
    for (const [url, resource] of this.resources) {
      if (now - resource.timestamp > maxAge) {
        // 清理图片资源
        if (resource.type === 'image') {
          const img = document.querySelector(`img[src="${url}"]`);
          if (img && !this.isElementInViewport(img)) {
            img.remove();
            this.resources.delete(url);
          }
        }
      }
    }
  }
  
  // 检查元素是否在视口中
  isElementInViewport(el) {
    const rect = el.getBoundingClientRect();
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  }
  
  // 销毁清理
  destroy() {
    // 清理所有观察器
    for (const observer of this.observers.values()) {
      observer.disconnect();
    }
    
    this.observers.clear();
    this.resources.clear();
  }
}

// 使用示例
const resourceManager = new AdvancedResourceManager();

// 页面卸载前保存状态
window.addEventListener('beforeunload', () => {
  // 使用sendBeacon发送性能数据
  const performanceData = resourceManager.performanceEntries;
  const data = JSON.stringify(performanceData);
  
  navigator.sendBeacon('/api/performance-metrics', data);
});

// 页面隐藏时清理资源
document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    // 页面隐藏,清理非关键资源
    resourceManager.cleanupUnusedResources();
  }
});

8.3 HTTP/2与HTTP/3的优化实践

HTTP/2和HTTP/3提供了更高效的传输协议,但需要正确配置才能发挥最大效果。

javascript 复制代码
// HTTP/2服务器推送配置示例
// Node.js + Express
const express = require('express');
const fs = require('fs');
const http2 = require('http2');
const path = require('path');

const app = express();

// 静态资源中间件,支持HTTP/2推送
const staticWithPush = (req, res, next) => {
  const filePath = path.join(__dirname, 'public', req.path);
  
  // 检查是否是静态资源
  if (fs.existsSync(filePath)) {
    const ext = path.extname(filePath);
    
    // 定义需要推送的资源
    const pushResources = {
      '/index.html': [
        '/static/css/main.css',
        '/static/js/main.js',
        '/static/images/logo.png'
      ],
      '/about.html': [
        '/static/css/about.css',
        '/static/js/about.js'
      ]
    };
    
    // 如果是HTML文件,推送相关资源
    if (ext === '.html' && res.push) {
      const resources = pushResources[req.path] || [];
      
      resources.forEach(resourcePath => {
        const push = res.push(resourcePath);
        const fullPath = path.join(__dirname, 'public', resourcePath);
        
        if (fs.existsSync(fullPath)) {
          push.writeHead(200);
          fs.createReadStream(fullPath).pipe(push);
        } else {
          push.end();
        }
      });
    }
  }
  
  next();
};

app.use(staticWithPush);
app.use(express.static('public'));

// 创建HTTP/2服务器
const serverOptions = {
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt'),
  allowHTTP1: true // 允许HTTP/1.1回退
};

const server = http2.createSecureServer(serverOptions, app);

server.listen(443, () => {
  console.log('HTTP/2服务器运行在 https://localhost');
});

// HTTP/2客户端优化
class HTTP2Client {
  constructor() {
    this.session = null;
    this.streams = new Map();
    this.pendingRequests = new Map();
  }
  
  // 连接到HTTP/2服务器
  async connect(url) {
    return new Promise((resolve, reject) => {
      const session = http2.connect(url);
      
      session.on('connect', () => {
        console.log('HTTP/2连接已建立');
        this.session = session;
        resolve(session);
      });
      
      session.on('error', (err) => {
        console.error('HTTP/2连接错误:', err);
        reject(err);
      });
      
      session.on('goaway', () => {
        console.log('收到GOAWAY帧,连接将关闭');
        this.cleanup();
      });
      
      // 监听设置帧,了解服务器能力
      session.on('remoteSettings', (settings) => {
        console.log('服务器设置:', settings);
      });
    });
  }
  
  // 发送请求
  async request(path, options = {}) {
    if (!this.session) {
      throw new Error('未连接到服务器');
    }
    
    return new Promise((resolve, reject) => {
      const headers = {
        ':path': path,
        ':method': options.method || 'GET',
        ':scheme': 'https',
        ':authority': new URL(options.url || window.location.origin).hostname,
        ...options.headers
      };
      
      // 创建流
      const stream = this.session.request(headers);
      const streamId = stream.id;
      
      // 存储流引用
      this.streams.set(streamId, stream);
      
      // 设置超时
      const timeout = options.timeout || 30000;
      const timeoutId = setTimeout(() => {
        stream.close(http2.constants.NGHTTP2_CANCEL);
        this.streams.delete(streamId);
        reject(new Error(`请求超时: ${path}`));
      }, timeout);
      
      // 收集响应数据
      let responseData = [];
      let responseHeaders = {};
      
      stream.on('response', (headers) => {
        responseHeaders = headers;
        
        // 检查服务器推送
        if (headers[':status'] === '200' && headers['content-type']?.includes('text/html')) {
          this.setupPushReceiver(stream);
        }
      });
      
      stream.on('data', (chunk) => {
        responseData.push(chunk);
      });
      
      stream.on('end', () => {
        clearTimeout(timeoutId);
        
        const response = {
          headers: responseHeaders,
          data: Buffer.concat(responseData),
          streamId
        };
        
        this.streams.delete(streamId);
        resolve(response);
      });
      
      stream.on('error', (err) => {
        clearTimeout(timeoutId);
        this.streams.delete(streamId);
        reject(err);
      });
      
      // 发送请求体(如果有)
      if (options.body) {
        stream.write(options.body);
      }
      
      stream.end();
    });
  }
  
  // 设置推送接收
  setupPushReceiver(stream) {
    stream.on('push', (pushHeaders, flags, rawHeaders) => {
      const pushedPath = pushHeaders[':path'];
      console.log(`收到服务器推送: ${pushedPath}`);
      
      const pushStream = this.session.request({
        ...pushHeaders,
        ':method': 'GET'
      });
      
      let pushData = [];
      
      pushStream.on('data', (chunk) => {
        pushData.push(chunk);
      });
      
      pushStream.on('end', () => {
        const data = Buffer.concat(pushData);
        
        // 缓存推送的资源
        this.cachePushedResource(pushedPath, data);
        
        console.log(`推送资源接收完成: ${pushedPath} (${data.length} bytes)`);
      });
    });
  }
  
  // 缓存推送的资源
  cachePushedResource(path, data) {
    // 实现资源缓存逻辑
    const cacheKey = `push-cache:${path}`;
    
    try {
      // 使用IndexedDB或Cache API存储
      if ('caches' in window) {
        caches.open('http2-push-cache').then(cache => {
          const response = new Response(data, {
            headers: { 'Content-Type': this.getContentType(path) }
          });
          
          cache.put(path, response);
        });
      }
      
      console.log(`资源已缓存: ${path}`);
    } catch (error) {
      console.error('缓存推送资源失败:', error);
    }
  }
  
  getContentType(path) {
    const ext = path.split('.').pop().toLowerCase();
    const types = {
      'css': 'text/css',
      'js': 'application/javascript',
      'png': 'image/png',
      'jpg': 'image/jpeg',
      'webp': 'image/webp',
      'html': 'text/html'
    };
    
    return types[ext] || 'application/octet-stream';
  }
  
  // 多路复用优化:批量请求
  async batchRequests(requests) {
    const promises = requests.map(req => 
      this.request(req.path, req.options)
    );
    
    // 使用Promise.all同时发送多个请求
    return Promise.all(promises);
  }
  
  // 优先级设置
  async requestWithPriority(path, priority, options = {}) {
    const weight = this.calculatePriorityWeight(priority);
    
    const stream = this.session.request({
      ':path': path,
      ':method': options.method || 'GET',
      ':scheme': 'https',
      ':authority': new URL(options.url || window.location.origin).hostname
    }, {
      weight,
      exclusive: priority === 'high',
      parent: 0
    });
    
    // ... 处理响应
  }
  
  calculatePriorityWeight(priority) {
    const weights = {
      'highest': 256,
      'high': 180,
      'medium': 120,
      'low': 60,
      'lowest': 1
    };
    
    return weights[priority] || 120;
  }
  
  // 连接管理
  async manageConnection() {
    // 监控连接状态
    setInterval(() => {
      if (this.session) {
        const { destroyed, closed } = this.session;
        
        if (destroyed || closed) {
          console.log('HTTP/2连接已关闭,尝试重连...');
          this.reconnect();
        }
      }
    }, 5000);
    
    // 流量控制监控
    this.session.on('flowControl', (stream) => {
      const windowSize = stream.state.localWindowSize;
      
      if (windowSize < 1024) {
        console.warn('流量控制窗口较小,可能影响性能');
      }
    });
  }
  
  // 清理资源
  cleanup() {
    // 关闭所有流
    for (const stream of this.streams.values()) {
      stream.close();
    }
    
    this.streams.clear();
    
    // 关闭会话
    if (this.session) {
      this.session.destroy();
      this.session = null;
    }
  }
  
  // 重连逻辑
  async reconnect() {
    this.cleanup();
    
    try {
      await this.connect(this.originalUrl);
      console.log('HTTP/2连接重连成功');
    } catch (error) {
      console.error('重连失败:', error);
      
      // 指数退避重试
      setTimeout(() => this.reconnect(), 5000);
    }
  }
}

// HTTP/3检测与回退
class HTTP3Detector {
  constructor() {
    this.supportsHTTP3 = false;
    this.checkHTTP3Support();
  }
  
  async checkHTTP3Support() {
    try {
      // 尝试使用HTTP/3连接
      const testUrl = 'https://http3.is/';
      const response = await fetch(testUrl, {
        method: 'HEAD',
        mode: 'no-cors'
      });
      
      // 检查Alt-Svc头部
      const altSvc = response.headers.get('Alt-Svc');
      
      if (altSvc && altSvc.includes('h3=')) {
        this.supportsHTTP3 = true;
        console.log('检测到HTTP/3支持');
      } else {
        console.log('未检测到HTTP/3支持,使用HTTP/2或HTTP/1.1');
      }
    } catch (error) {
      console.log('HTTP/3检测失败:', error);
    }
  }
  
  // 获取最佳协议
  getBestProtocol() {
    if (this.supportsHTTP3) {
      return 'h3';
    } else if (window.performance) {
      // 检查是否已在使用HTTP/2
      const entries = performance.getEntriesByType('navigation');
      if (entries.length > 0) {
        const protocol = entries[0].nextHopProtocol;
        if (protocol && protocol.startsWith('h2')) {
          return 'h2';
        }
      }
    }
    
    return 'http/1.1';
  }
  
  // 应用协议优化
  applyProtocolOptimizations() {
    const protocol = this.getBestProtocol();
    
    switch (protocol) {
      case 'h3':
        this.applyHTTP3Optimizations();
        break;
      case 'h2':
        this.applyHTTP2Optimizations();
        break;
      default:
        this.applyHTTP11Optimizations();
    }
  }
  
  applyHTTP3Optimizations() {
    console.log('应用HTTP/3优化策略');
    
    // HTTP/3特有优化
    // 1. 更多的并发请求(无队头阻塞)
    // 2. 0-RTT连接恢复
    // 3. 改进的拥塞控制
    
    // 调整资源加载策略
    this.adjustResourceLoadingForHTTP3();
  }
  
  applyHTTP2Optimizations() {
    console.log('应用HTTP/2优化策略');
    
    // HTTP/2优化
    // 1. 域名收缩(减少DNS查询)
    // 2. 利用多路复用
    // 3. 服务器推送
    
    // 合并小文件
    this.mergeSmallResources();
  }
  
  applyHTTP11Optimizations() {
    console.log('应用HTTP/1.1优化策略');
    
    // HTTP/1.1传统优化
    // 1. 域名分片(突破浏览器并发限制)
    // 2. 资源合并
    // 3. 减少请求数
    
    this.enableHTTP11Optimizations();
  }
  
  adjustResourceLoadingForHTTP3() {
    // HTTP/3下可以更激进地并行加载
    const scripts = document.querySelectorAll('script[defer]');
    
    scripts.forEach(script => {
      // 移除defer,直接加载
      script.removeAttribute('defer');
    });
  }
  
  mergeSmallResources() {
    // 合并小的CSS和JS文件
    // 实际项目中应在构建时完成
    console.log('建议在构建时合并小文件以优化HTTP/2');
  }
  
  enableHTTP11Optimizations() {
    // 传统优化:域名分片
    const domains = [
      'static1.example.com',
      'static2.example.com',
      'static3.example.com'
    ];
    
    // 动态调整资源域名
    const resources = document.querySelectorAll('img, script, link[rel="stylesheet"]');
    
    resources.forEach((resource, index) => {
      const domain = domains[index % domains.length];
      const src = resource.src || resource.href;
      
      if (src) {
        const url = new URL(src, window.location.origin);
        url.hostname = domain;
        
        if (resource.src) {
          resource.src = url.toString();
        } else {
          resource.href = url.toString();
        }
      }
    });
  }
}

// 使用示例
document.addEventListener('DOMContentLoaded', async () => {
  // 检测HTTP/3支持
  const http3Detector = new HTTP3Detector();
  
  // 延迟执行,等待检测完成
  setTimeout(() => {
    http3Detector.applyProtocolOptimizations();
  }, 1000);
  
  // 如果支持HTTP/2,使用HTTP/2客户端
  if (http3Detector.getBestProtocol() === 'h2') {
    const http2Client = new HTTP2Client();
    
    try {
      await http2Client.connect('https://api.example.com');
      
      // 批量请求示例
      const responses = await http2Client.batchRequests([
        { path: '/api/user', options: { method: 'GET' } },
        { path: '/api/products', options: { method: 'GET' } },
        { path: '/api/settings', options: { method: 'GET' } }
      ]);
      
      console.log('批量请求完成:', responses);
      
      // 管理连接
      http2Client.manageConnection();
      
    } catch (error) {
      console.error('HTTP/2连接失败:', error);
    }
  }
});

九、性能监控与持续优化

9.1 全面的性能度量指标

现代浏览器提供了丰富的性能API,我们可以利用这些API构建完整的性能监控体系。

javascript 复制代码
// 综合性能监控器
class PerformanceMonitor {
  constructor() {
    this.metrics = new Map();
    this.observers = [];
    this.thresholds = {
      fcp: 2000,      // 首次内容绘制
      lcp: 4000,      // 最大内容绘制
      fid: 100,       // 首次输入延迟
      cls: 0.1,       // 累积布局偏移
      ttfb: 600,      // 首字节时间
      tti: 5000       // 可交互时间
    };
    
    this.init();
  }
  
  init() {
    // 监听所有性能指标
    this.setupPerformanceObservers();
    
    // 监听用户交互
    this.setupUserInteractionTracking();
    
    // 监控内存使用
    this.setupMemoryMonitoring();
    
    // 网络状态监控
    this.setupNetworkMonitoring();
  }
  
  setupPerformanceObservers() {
    // 1. 首次绘制相关
    this.setupPaintObserver();
    
    // 2. 最大内容绘制
    this.setupLCPObserver();
    
    // 3. 首次输入延迟
    this.setupFIDObserver();
    
    // 4. 累积布局偏移
    this.setupCLSObserver();
    
    // 5. 长任务监控
    this.setupLongTaskObserver();
    
    // 6. 布局偏移
    this.setupLayoutShiftObserver();
  }
  
  setupPaintObserver() {
    if ('PerformanceObserver' in window) {
      const observer = new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
          if (entry.name === 'first-paint') {
            this.recordMetric('fp', entry.startTime);
            this.checkThreshold('fp', entry.startTime);
          }
          if (entry.name === 'first-contentful-paint') {
            this.recordMetric('fcp', entry.startTime);
            this.checkThreshold('fcp', entry.startTime);
          }
        }
      });
      
      observer.observe({ entryTypes: ['paint'] });
      this.observers.push(observer);
    }
  }
  
  setupLCPObserver() {
    if ('PerformanceObserver' in window) {
      let lcpValue = 0;
      
      const observer = new PerformanceObserver((list) => {
        const entries = list.getEntries();
        const lastEntry = entries[entries.length - 1];
        
        if (lastEntry) {
          lcpValue = lastEntry.startTime;
          this.recordMetric('lcp', lcpValue);
          this.checkThreshold('lcp', lcpValue);
        }
      });
      
      observer.observe({ entryTypes: ['largest-contentful-paint'] });
      this.observers.push(observer);
      
      // 页面隐藏时记录最终LCP
      document.addEventListener('visibilitychange', () => {
        if (document.visibilityState === 'hidden') {
          this.recordMetric('final-lcp', lcpValue);
        }
      });
    }
  }
  
  setupFIDObserver() {
    if ('PerformanceObserver' in window) {
      const observer = new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
          const fid = entry.processingStart - entry.startTime;
          
          this.recordMetric('fid', fid);
          this.checkThreshold('fid', fid);
          
          // 记录交互详情
          this.recordInteractionDetail(entry);
        }
      });
      
      observer.observe({ entryTypes: ['first-input'] });
      this.observers.push(observer);
    }
  }
  
  setupCLSObserver() {
    if ('PerformanceObserver' in window) {
      let clsValue = 0;
      let sessionValue = 0;
      let sessionEntries = [];
      
      const observer = new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
          // 忽略最近500ms内的用户输入
          if (!entry.hadRecentInput) {
            const firstSessionEntry = sessionEntries[0];
            const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
            
            // 如果条目间隔超过1秒或位置差距超过1秒,开始新的会话
            if (sessionEntries.length === 0 ||
                entry.startTime - lastSessionEntry.startTime < 1000 &&
                entry.startTime - firstSessionEntry.startTime < 5000) {
              sessionValue += entry.value;
              sessionEntries.push(entry);
            } else {
              // 新会话
              sessionValue = entry.value;
              sessionEntries = [entry];
            }
            
            // 更新最大会话值
            if (sessionValue > clsValue) {
              clsValue = sessionValue;
              this.recordMetric('cls', clsValue);
              this.checkThreshold('cls', clsValue);
            }
          }
        }
      });
      
      observer.observe({ entryTypes: ['layout-shift'] });
      this.observers.push(observer);
    }
  }
  
  setupLongTaskObserver() {
    if ('PerformanceObserver' in window) {
      const observer = new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
          this.recordMetric('long-task', entry.duration);
          
          // 记录长任务详情
          this.recordLongTaskDetail(entry);
          
          if (entry.duration > 50) {
            console.warn(`检测到长任务: ${entry.duration.toFixed(2)}ms`);
            this.reportPerformanceIssue('long_task', {
              duration: entry.duration,
              name: entry.name
            });
          }
        }
      });
      
      observer.observe({ entryTypes: ['longtask'] });
      this.observers.push(observer);
    }
  }
  
  setupUserInteractionTracking() {
    // 跟踪用户关键交互
    const interactions = ['click', 'keydown', 'tap'];
    
    interactions.forEach(eventType => {
      document.addEventListener(eventType, (event) => {
        const interactionTime = performance.now();
        
        // 记录交互
        this.recordInteraction({
          type: eventType,
          target: event.target?.tagName,
          time: interactionTime,
          path: this.getElementPath(event.target)
        });
      }, { passive: true });
    });
  }
  
  setupMemoryMonitoring() {
    // 监控内存使用
    if ('memory' in performance) {
      setInterval(() => {
        const memory = performance.memory;
        
        this.recordMetric('memory-used', memory.usedJSHeapSize);
        this.recordMetric('memory-total', memory.totalJSHeapSize);
        this.recordMetric('memory-limit', memory.jsHeapSizeLimit);
        
        // 检查内存泄漏
        this.checkMemoryLeak();
      }, 10000);
    }
  }
  
  setupNetworkMonitoring() {
    // 网络信息API
    if ('connection' in navigator) {
      const connection = navigator.connection;
      
      connection.addEventListener('change', () => {
        this.recordNetworkInfo();
      });
      
      this.recordNetworkInfo();
    }
    
    // 监听离线/在线状态
    window.addEventListener('online', () => {
      this.recordMetric('network-status', 'online');
    });
    
    window.addEventListener('offline', () => {
      this.recordMetric('network-status', 'offline');
    });
  }
  
  recordNetworkInfo() {
    if ('connection' in navigator) {
      const connection = navigator.connection;
      
      this.recordMetric('effective-type', connection.effectiveType);
      this.recordMetric('downlink', connection.downlink);
      this.recordMetric('rtt', connection.rtt);
      this.recordMetric('save-data', connection.saveData);
    }
  }
  
  // 记录指标
  recordMetric(name, value, tags = {}) {
    const timestamp = Date.now();
    const metric = {
      name,
      value,
      timestamp,
      tags: {
        url: window.location.href,
        userAgent: navigator.userAgent,
        ...tags
      }
    };
    
    this.metrics.set(`${name}-${timestamp}`, metric);
    
    // 发送到监控服务
    this.sendMetric(metric);
    
    // 本地存储(用于离线分析)
    this.storeMetricLocally(metric);
  }
  
  // 检查阈值
  checkThreshold(name, value) {
    const threshold = this.thresholds[name];
    
    if (threshold && value > threshold) {
      console.warn(`性能阈值警告: ${name}=${value}ms, 阈值=${threshold}ms`);
      
      this.reportPerformanceIssue('threshold_exceeded', {
        metric: name,
        value,
        threshold
      });
    }
  }
  
  // 报告性能问题
  reportPerformanceIssue(type, data) {
    const issue = {
      type,
      data,
      timestamp: Date.now(),
      url: window.location.href,
      userAgent: navigator.userAgent
    };
    
    // 使用sendBeacon发送,即使页面卸载也能发送
    if (navigator.sendBeacon) {
      const blob = new Blob([JSON.stringify(issue)], {
        type: 'application/json'
      });
      
      navigator.sendBeacon('/api/performance-issues', blob);
    }
    
    // 触发自定义事件
    window.dispatchEvent(new CustomEvent('performance-issue', {
      detail: issue
    }));
  }
  
  // 发送指标到监控服务
  sendMetric(metric) {
    // 批量发送以减少请求
    if (!this.sendQueue) {
      this.sendQueue = [];
      this.sendTimer = null;
    }
    
    this.sendQueue.push(metric);
    
    if (!this.sendTimer) {
      this.sendTimer = setTimeout(() => {
        this.flushMetrics();
      }, 1000);
    }
  }
  
  // 批量发送指标
  flushMetrics() {
    if (this.sendQueue.length > 0) {
      const metrics = [...this.sendQueue];
      this.sendQueue = [];
      
      if (navigator.sendBeacon) {
        const blob = new Blob([JSON.stringify(metrics)], {
          type: 'application/json'
        });
        
        navigator.sendBeacon('/api/performance-metrics', blob);
      }
    }
    
    this.sendTimer = null;
  }
  
  // 本地存储指标
  storeMetricLocally(metric) {
    try {
      // 使用IndexedDB存储
      if ('indexedDB' in window) {
        this.storeInIndexedDB(metric);
      } else {
        // 回退到localStorage
        this.storeInLocalStorage(metric);
      }
    } catch (error) {
      console.error('存储指标失败:', error);
    }
  }
  
  // 获取性能报告
  getPerformanceReport() {
    const report = {
      timestamp: Date.now(),
      url: window.location.href,
      metrics: {},
      issues: [],
      recommendations: []
    };
    
    // 收集所有指标
    for (const metric of this.metrics.values()) {
      if (!report.metrics[metric.name]) {
        report.metrics[metric.name] = [];
      }
      report.metrics[metric.name].push(metric.value);
    }
    
    // 计算关键指标
    this.calculateCoreWebVitals(report);
    
    // 生成建议
    this.generateRecommendations(report);
    
    return report;
  }
  
  calculateCoreWebVitals(report) {
    // 计算核心Web指标
    const coreVitals = {};
    
    // LCP
    const lcpValues = report.metrics.lcp || [];
    if (lcpValues.length > 0) {
      coreVitals.lcp = Math.max(...lcpValues);
    }
    
    // FID
    const fidValues = report.metrics.fid || [];
    if (fidValues.length > 0) {
      coreVitals.fid = Math.max(...fidValues);
    }
    
    // CLS
    const clsValues = report.metrics.cls || [];
    if (clsValues.length > 0) {
      coreVitals.cls = Math.max(...clsValues);
    }
    
    report.coreWebVitals = coreVitals;
  }
  
  generateRecommendations(report) {
    const recommendations = [];
    
    // 基于指标生成优化建议
    if (report.coreWebVitals?.lcp > 4000) {
      recommendations.push({
        type: 'lcp_optimization',
        severity: 'high',
        message: '最大内容绘制时间过长,建议优化首屏图片加载和服务器响应时间',
        actions: [
          '使用图片懒加载',
          '优化服务器配置',
          '使用CDN加速',
          '预加载关键资源'
        ]
      });
    }
    
    if (report.coreWebVitals?.fid > 100) {
      recommendations.push({
        type: 'fid_optimization',
        severity: 'medium',
        message: '首次输入延迟较高,建议优化JavaScript执行',
        actions: [
          '代码分割和懒加载',
          '优化长任务',
          '使用Web Workers处理复杂计算'
        ]
      });
    }
    
    if (report.coreWebVitals?.cls > 0.1) {
      recommendations.push({
        type: 'cls_optimization',
        severity: 'high',
        message: '累积布局偏移严重,影响用户体验',
        actions: [
          '为图片和视频设置尺寸',
          '避免动态插入内容',
          '使用transform代替top/left动画'
        ]
      });
    }
    
    report.recommendations = recommendations;
  }
  
  // 清理资源
  destroy() {
    // 清理所有观察器
    this.observers.forEach(observer => {
      try {
        observer.disconnect();
      } catch (e) {
        // 忽略错误
      }
    });
    
    this.observers = [];
    
    // 发送剩余指标
    this.flushMetrics();
    
    // 清理定时器
    if (this.sendTimer) {
      clearTimeout(this.sendTimer);
    }
  }
}

// 使用示例
const performanceMonitor = new PerformanceMonitor();

// 页面卸载前获取报告
window.addEventListener('beforeunload', () => {
  const report = performanceMonitor.getPerformanceReport();
  
  // 发送最终报告
  if (navigator.sendBeacon) {
    const blob = new Blob([JSON.stringify(report)], {
      type: 'application/json'
    });
    
    navigator.sendBeacon('/api/performance-report', blob);
  }
  
  // 清理
  performanceMonitor.destroy();
});

// 定期获取报告(用于开发调试)
setInterval(() => {
  const report = performanceMonitor.getPerformanceReport();
  console.log('性能报告:', report);
}, 30000);

9.2 自动化性能测试与CI/CD集成

自动化性能测试可以确保代码变更不会引入性能回归。

javascript 复制代码
// Lighthouse CI配置示例
// .lighthouserc.js
module.exports = {
  ci: {
    collect: {
      // 收集配置
      numberOfRuns: 3,                    // 每次测试运行3次取平均值
      startServerCommand: 'npm run start', // 启动开发服务器
      url: [
        'http://localhost:3000',
        'http://localhost:3000/about',
        'http://localhost:3000/products'
      ],
      settings: {
        // 设备模拟
        emulatedFormFactor: 'mobile',     // 移动设备
        screenEmulation: {
          mobile: true,
          width: 375,
          height: 667,
          deviceScaleFactor: 2,
          disabled: false
        },
        // 网络限制
        throttling: {
          rttMs: 150,                     // 往返延迟
          throughputKbps: 1638.4,         // 吞吐量
          requestLatencyMs: 150,
          downloadThroughputKbps: 1474.56,
          uploadThroughputKbps: 675,
          cpuSlowdownMultiplier: 4        // CPU减速
        },
        // 其他设置
        onlyCategories: ['performance', 'accessibility', 'best-practices', 'seo'],
        skipAudits: ['uses-http2']        // 跳过特定审计
      }
    },
    assert: {
      // 断言配置
      assertions: {
        // 核心Web指标
        'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 4000 }],
        'first-meaningful-paint': ['error', { maxNumericValue: 3000 }],
        'speed-index': ['error', { maxNumericValue: 4300 }],
        'interactive': ['error', { maxNumericValue: 5000 }],
        'total-blocking-time': ['error', { maxNumericValue: 300 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
        
        // 最佳实践
        'uses-responsive-images': ['warn', { minScore: 0.8 }],
        'offscreen-images': ['warn', { minScore: 0.8 }],
        'render-blocking-resources': ['warn', { minScore: 0.8 }],
        
        // SEO
        'document-title': ['error', { minScore: 1 }],
        'meta-description': ['warn', { minScore: 0.9 }]
      }
    },
    upload: {
      // 上传配置
      target: 'temporary-public-storage', // 临时存储
      // 或上传到Lighthouse服务器
      // target: 'lhci',
      // serverBaseUrl: 'https://your-lighthouse-server.com',
      // token: 'your-token'
    }
  }
};

// GitHub Actions工作流配置
// .github/workflows/lighthouse.yml
name: Lighthouse CI
on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run Lighthouse CI
        uses: treosh/lighthouse-ci-action@v9
        with:
          configPath: './.lighthouserc.js'
          uploadArtifacts: true
          temporaryPublicStorage: true
          runs: 3
          
      - name: Performance Budget Check
        run: |
          # 自定义性能预算检查
          npm run check-performance-budget
          
      - name: Generate Report
        if: always()
        run: |
          # 生成可读报告
          npm run generate-performance-report
          
      - name: Upload Lighthouse Results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: lighthouse-results
          path: |
            .lighthouseci/
            reports/
          retention-days: 30

// 性能预算检查脚本
// scripts/check-performance-budget.js
const fs = require('fs');
const path = require('path');

// 性能预算定义
const PERFORMANCE_BUDGET = {
  'first-contentful-paint': 2000,
  'largest-contentful-paint': 4000,
  'first-meaningful-paint': 3000,
  'speed-index': 4300,
  'interactive': 5000,
  'total-blocking-time': 300,
  'cumulative-layout-shift': 0.1,
  
  // 资源大小预算
  'resource-sizes': {
    'total': 5 * 1024 * 1024, // 5MB
    'javascript': 300 * 1024, // 300KB
    'css': 100 * 1024,        // 100KB
    'images': 4 * 1024 * 1024 // 4MB
  }
};

// 检查Lighthouse结果
function checkLighthouseResults() {
  const resultsPath = path.join(__dirname, '..', '.lighthouseci');
  const files = fs.readdirSync(resultsPath);
  
  let hasBudgetViolation = false;
  const violations = [];
  
  files.forEach(file => {
    if (file.endsWith('.json')) {
      const filePath = path.join(resultsPath, file);
      const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
      
      // 检查各项指标
      const audits = data.audits || {};
      
      Object.keys(PERFORMANCE_BUDGET).forEach(key => {
        if (key === 'resource-sizes') return;
        
        const audit = audits[key];
        if (audit && audit.numericValue) {
          const budget = PERFORMANCE_BUDGET[key];
          const value = audit.numericValue;
          
          if (value > budget) {
            hasBudgetViolation = true;
            violations.push({
              url: data.finalUrl,
              metric: key,
              value: Math.round(value),
              budget: budget,
              difference: Math.round(value - budget)
            });
          }
        }
      });
      
      // 检查资源大小
      const resourceAudit = audits['resource-summary'];
      if (resourceAudit && resourceAudit.details) {
        const resources = resourceAudit.details.items || [];
        
        resources.forEach(resource => {
          const type = resource.resourceType;
          const size = resource.size;
          
          if (PERFORMANCE_BUDGET['resource-sizes'][type] && 
              size > PERFORMANCE_BUDGET['resource-sizes'][type]) {
            hasBudgetViolation = true;
            violations.push({
              url: data.finalUrl,
              metric: `${type}-size`,
              value: Math.round(size / 1024),
              budget: Math.round(PERFORMANCE_BUDGET['resource-sizes'][type] / 1024),
              difference: Math.round((size - PERFORMANCE_BUDGET['resource-sizes'][type]) / 1024),
              resource: resource.url
            });
          }
        });
      }
    }
  });
  
  // 生成报告
  if (violations.length > 0) {
    console.error('🚨 性能预算违规检测到!');
    console.table(violations);
    
    // 生成详细的HTML报告
    generateHTMLReport(violations);
    
    // 在CI中标记为失败
    process.exit(1);
  } else {
    console.log('✅ 所有性能指标都在预算内');
  }
}

// 生成HTML报告
function generateHTMLReport(violations) {
  const report = `
<!DOCTYPE html>
<html>
<head>
  <title>性能预算违规报告</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    .violation { 
      border: 2px solid #ff6b6b; 
      padding: 15px; 
      margin: 10px 0; 
      border-radius: 5px;
    }
    .metric { font-weight: bold; color: #ff6b6b; }
    .url { color: #666; font-size: 0.9em; }
    .budget { color: #4ecdc4; }
    .exceeded { color: #ff6b6b; }
    table { 
      width: 100%; 
      border-collapse: collapse; 
      margin: 20px 0;
    }
    th, td { 
      padding: 10px; 
      text-align: left; 
      border-bottom: 1px solid #ddd;
    }
    th { background-color: #f2f2f2; }
  </style>
</head>
<body>
  <h1>性能预算违规报告</h1>
  <p>生成时间: ${new Date().toLocaleString()}</p>
  
  <table>
    <thead>
      <tr>
        <th>URL</th>
        <th>指标</th>
        <th>实际值</th>
        <th>预算值</th>
        <th>超出</th>
      </tr>
    </thead>
    <tbody>
      ${violations.map(v => `
        <tr>
          <td class="url">${v.url}</td>
          <td class="metric">${v.metric}</td>
          <td>${v.value}${v.metric.includes('size') ? 'KB' : 'ms'}</td>
          <td class="budget">${v.budget}${v.metric.includes('size') ? 'KB' : 'ms'}</td>
          <td class="exceeded">+${v.difference}${v.metric.includes('size') ? 'KB' : 'ms'}</td>
        </tr>
      `).join('')}
    </tbody>
  </table>
  
  <h2>优化建议</h2>
  <div class="recommendations">
    ${generateRecommendations(violations)}
  </div>
</body>
</html>
  `;
  
  const reportPath = path.join(__dirname, '..', 'reports', 'performance-violations.html');
  fs.writeFileSync(reportPath, report);
  console.log(`📊 报告已生成: ${reportPath}`);
}

// 生成优化建议
function generateRecommendations(violations) {
  const recommendations = [];
  
  violations.forEach(violation => {
    const { metric, value, budget } = violation;
    
    if (metric.includes('fcp') || metric.includes('lcp')) {
      recommendations.push(`
        <div class="violation">
          <h3>${metric.toUpperCase()} 优化建议</h3>
          <p>当前值: ${value}ms | 预算: ${budget}ms</p>
          <ul>
            <li>优化服务器响应时间</li>
            <li>使用CDN加速静态资源</li>
            <li>预加载关键资源</li>
            <li>优化首屏图片(使用WebP/AVIF格式)</li>
            <li>移除渲染阻塞资源</li>
          </ul>
        </div>
      `);
    } else if (metric.includes('fid') || metric.includes('interactive')) {
      recommendations.push(`
        <div class="violation">
          <h3>${metric.toUpperCase()} 优化建议</h3>
          <p>当前值: ${value}ms | 预算: ${budget}ms</p>
          <ul>
            <li>代码分割和懒加载</li>
            <li>优化JavaScript执行时间</li>
            <li>使用Web Workers处理复杂计算</li>
            <li>避免长任务(超过50ms的任务)</li>
            <li>优化第三方脚本加载</li>
          </ul>
        </div>
      `);
    } else if (metric.includes('size')) {
      recommendations.push(`
        <div class="violation">
          <h3>资源大小优化建议</h3>
          <p>当前值: ${value}KB | 预算: ${budget}KB</p>
          <ul>
            <li>压缩图片资源</li>
            <li>使用现代图片格式(WebP/AVIF)</li>
            <li>Tree Shaking移除未使用代码</li>
            <li>代码分割按需加载</li>
            <li>Gzip/Brotli压缩</li>
          </ul>
        </div>
      `);
    }
  });
  
  return recommendations.join('');
}

// 运行检查
checkLighthouseResults();

十、实战优化流程与性能预算

10.1 四步优化法实践

javascript 复制代码
// 性能优化工作流管理器
class PerformanceOptimizationWorkflow {
  constructor() {
    this.currentStep = 'measure';
    this.optimizationHistory = [];
    this.performanceBaseline = null;
    
    this.workflowSteps = {
      measure: this.measurePerformance.bind(this),
      analyze: this.analyzePerformance.bind(this),
      implement: this.implementOptimizations.bind(this),
      monitor: this.monitorPerformance.bind(this)
    };
  }
  
  // 第一步:度量 - 全面评估当前性能
  async measurePerformance() {
    console.log('🚀 第一步:性能度量');
    
    const metrics = {
      // 加载性能
      loading: await this.measureLoadingPerformance(),
      
      // 运行时性能
      runtime: await this.measureRuntimePerformance(),
      
      // 资源使用
      resources: await this.measureResourceUsage(),
      
      // 用户体验指标
      userExperience: await this.measureUserExperience()
    };
    
    // 建立性能基线
    this.performanceBaseline = metrics;
    
    // 生成初始报告
    this.generateInitialReport(metrics);
    
    // 进入下一步
    this.currentStep = 'analyze';
    await this.workflowSteps[this.currentStep]();
    
    return metrics;
  }
  
  async measureLoadingPerformance() {
    // 使用Performance API收集加载指标
    const navigation = performance.getEntriesByType('navigation')[0];
    const resources = performance.getEntriesByType('resource');
    
    return {
      ttfb: navigation?.responseStart - navigation?.requestStart || 0,
      fcp: this.getMetricValue('first-contentful-paint'),
      lcp: this.getMetricValue('largest-contentful-paint'),
      loadTime: navigation?.loadEventEnd - navigation?.startTime || 0,
      resourceCount: resources.length,
      totalResourceSize: resources.reduce((sum, r) => sum + (r.transferSize || 0), 0)
    };
  }
  
  async measureRuntimePerformance() {
    // 收集运行时性能数据
    return {
      fps: await this.calculateFPS(),
      memoryUsage: this.getMemoryUsage(),
      longTasks: this.getLongTasks(),
      eventListeners: this.countEventListeners()
    };
  }
  
  async measureResourceUsage() {
    // 分析资源使用情况
    const resources = performance.getEntriesByType('resource');
    
    const resourceByType = resources.reduce((acc, resource) => {
      const type = resource.initiatorType;
      if (!acc[type]) acc[type] = { count: 0, size: 0 };
      acc[type].count++;
      acc[type].size += resource.transferSize || 0;
      return acc;
    }, {});
    
    return {
      byType: resourceByType,
      unusedResources: await this.findUnusedResources()
    };
  }
  
  async measureUserExperience() {
    // 用户体验指标
    return {
      fid: this.getMetricValue('first-input-delay'),
      cls: this.getMetricValue('cumulative-layout-shift'),
      inp: await this.calculateINP(), // 交互到下次绘制
      scrollJank: await this.measureScrollJank()
    };
  }
  
  // 第二步:分析 - 定位性能瓶颈
  async analyzePerformance() {
    console.log('🔍 第二步:性能分析');
    
    const bottlenecks = {
      network: await this.analyzeNetworkBottlenecks(),
      rendering: await this.analyzeRenderingBottlenecks(),
      javascript: await this.analyzeJavaScriptBottlenecks(),
      memory: await this.analyzeMemoryBottlenecks()
    };
    
    // 生成瓶颈报告
    this.generateBottleneckReport(bottlenecks);
    
    // 确定优化优先级
    const optimizationPriority = this.prioritizeOptimizations(bottlenecks);
    
    // 进入下一步
    this.currentStep = 'implement';
    this.optimizationPriority = optimizationPriority;
    await this.workflowSteps[this.currentStep]();
    
    return { bottlenecks, optimizationPriority };
  }
  
  async analyzeNetworkBottlenecks() {
    const bottlenecks = [];
    const resources = performance.getEntriesByType('resource');
    
    // 分析慢资源
    resources.forEach(resource => {
      if (resource.duration > 1000) { // 超过1秒
        bottlenecks.push({
          type: 'slow_resource',
          url: resource.name,
          duration: resource.duration,
          size: resource.transferSize || 0,
          suggestion: '考虑压缩、缓存或CDN加速'
        });
      }
    });
    
    // 检查缓存效率
    const cacheEfficiency = await this.calculateCacheEfficiency();
    if (cacheEfficiency < 0.7) {
      bottlenecks.push({
        type: 'cache_inefficient',
        efficiency: cacheEfficiency,
        suggestion: '优化缓存策略,增加缓存命中率'
      });
    }
    
    return bottlenecks;
  }
  
  async analyzeRenderingBottlenecks() {
    const bottlenecks = [];
    
    // 检查强制同步布局
    const forcedReflows = await this.detectForcedReflows();
    if (forcedReflows.length > 0) {
      bottlenecks.push({
        type: 'forced_reflows',
        count: forcedReflows.length,
        details: forcedReflows,
        suggestion: '避免在循环中读取布局属性,批量DOM操作'
      });
    }
    
    // 检查大面积的绘制
    const largePaints = await this.detectLargePaints();
    if (largePaints.length > 0) {
      bottlenecks.push({
        type: 'large_paints',
        count: largePaints.length,
        suggestion: '减少重绘区域,使用CSS contain属性'
      });
    }
    
    return bottlenecks;
  }
  
  // 第三步:实施 - 执行优化措施
  async implementOptimizations() {
    console.log('⚡ 第三步:实施优化');
    
    if (!this.optimizationPriority) {
      console.warn('未找到优化优先级,请先运行分析步骤');
      return;
    }
    
    const results = {
      implemented: [],
      skipped: [],
      failed: []
    };
    
    // 按优先级实施优化
    for (const optimization of this.optimizationPriority) {
      try {
        console.log(`实施优化: ${optimization.name}`);
        
        const success = await this.applyOptimization(optimization);
        
        if (success) {
          results.implemented.push(optimization);
          this.recordOptimization(optimization, 'success');
        } else {
          results.failed.push(optimization);
          this.recordOptimization(optimization, 'failed');
        }
        
        // 每次优化后重新测量
        await this.measureAfterOptimization(optimization);
        
      } catch (error) {
        console.error(`优化实施失败: ${optimization.name}`, error);
        results.failed.push({ ...optimization, error: error.message });
      }
    }
    
    // 生成优化报告
    this.generateOptimizationReport(results);
    
    // 进入下一步
    this.currentStep = 'monitor';
    await this.workflowSteps[this.currentStep]();
    
    return results;
  }
  
  async applyOptimization(optimization) {
    // 根据优化类型应用不同的策略
    switch (optimization.type) {
      case 'network':
        return await this.applyNetworkOptimization(optimization);
      case 'rendering':
        return await this.applyRenderingOptimization(optimization);
      case 'javascript':
        return await this.applyJavaScriptOptimization(optimization);
      case 'resource':
        return await this.applyResourceOptimization(optimization);
      default:
        return false;
    }
  }
  
  async applyNetworkOptimization(optimization) {
    switch (optimization.subtype) {
      case 'cache_optimization':
        // 优化缓存策略
        return this.optimizeCacheStrategy();
      case 'resource_compression':
        // 启用资源压缩
        return this.enableResourceCompression();
      case 'cdn_deployment':
        // 部署到CDN
        return this.deployToCDN();
      default:
        return false;
    }
  }
  
  async applyRenderingOptimization(optimization) {
    switch (optimization.subtype) {
      case 'reduce_reflow':
        // 减少重排
        return this.reduceReflows();
      case 'optimize_animations':
        // 优化动画性能
        return this.optimizeAnimations();
      case 'virtualize_lists':
        // 虚拟化长列表
        return this.virtualizeLongLists();
      default:
        return false;
    }
  }
  
  // 第四步:监控 - 持续性能监控
  async monitorPerformance() {
    console.log('📊 第四步:持续监控');
    
    // 建立持续监控
    const monitoring = {
      realtime: this.setupRealtimeMonitoring(),
      alerts: this.setupPerformanceAlerts(),
      reporting: this.setupAutomatedReporting(),
      regression: this.setupRegressionDetection()
    };
    
    // 生成监控仪表板
    await this.generateMonitoringDashboard();
    
    // 记录工作流完成
    this.recordWorkflowCompletion();
    
    return monitoring;
  }
  
  setupRealtimeMonitoring() {
    // 实时性能监控
    const monitor = new PerformanceMonitor();
    
    // 关键指标监控
    setInterval(() => {
      const metrics = monitor.getCurrentMetrics();
      this.updateRealtimeDashboard(metrics);
      
      // 检查异常
      this.checkForAnomalies(metrics);
    }, 5000);
    
    return monitor;
  }
  
  setupPerformanceAlerts() {
    // 性能告警系统
    const thresholds = {
      lcp: 4000,
      fid: 100,
      cls: 0.1,
      memory: 0.8, // 80%内存使用
      errors: 10   // 每分钟错误数
    };
    
    // 设置监控
    this.setupThresholdAlerts(thresholds);
    
    return thresholds;
  }
  
  setupAutomatedReporting() {
    // 自动化报告
    const reportSchedule = {
      daily: this.generateDailyReport.bind(this),
      weekly: this.generateWeeklyReport.bind(this),
      monthly: this.generateMonthlyReport.bind(this)
    };
    
    // 设置定时任务
    this.scheduleReports(reportSchedule);
    
    return reportSchedule;
  }
  
  setupRegressionDetection() {
    // 性能回归检测
    const regressionConfig = {
      sensitivity: 0.1, // 10%变化视为回归
      historySize: 100,  // 保留100个历史记录
      checkInterval: 300000 // 每5分钟检查一次
    };
    
    this.detector = new RegressionDetector(regressionConfig);
    
    // 开始检测
    this.detector.start();
    
    return regressionConfig;
  }
  
  // 辅助方法
  getMetricValue(metricName) {
    const entries = performance.getEntriesByName(metricName);
    return entries.length > 0 ? entries[0].startTime : null;
  }
  
  async calculateFPS() {
    return new Promise(resolve => {
      let frameCount = 0;
      let startTime = performance.now();
      
      const countFrames = () => {
        frameCount++;
        const currentTime = performance.now();
        
        if (currentTime - startTime >= 1000) {
          resolve(frameCount);
        } else {
          requestAnimationFrame(countFrames);
        }
      };
      
      requestAnimationFrame(countFrames);
    });
  }
  
  getMemoryUsage() {
    if ('memory' in performance) {
      const memory = performance.memory;
      return {
        used: memory.usedJSHeapSize,
        total: memory.totalJSHeapSize,
        limit: memory.jsHeapSizeLimit,
        percentage: memory.usedJSHeapSize / memory.jsHeapSizeLimit
      };
    }
    return null;
  }
  
  getLongTasks() {
    const entries = performance.getEntriesByType('longtask');
    return entries.filter(entry => entry.duration > 50);
  }
  
  countEventListeners() {
    // 估算事件监听器数量
    let count = 0;
    const allElements = document.querySelectorAll('*');
    
    allElements.forEach(element => {
      // 获取元素的事件监听器(近似值)
      const listeners = getEventListeners(element);
      if (listeners) {
        count += Object.values(listeners).flat().length;
      }
    });
    
    return count;
  }
  
  // 记录优化历史
  recordOptimization(optimization, status) {
    this.optimizationHistory.push({
      ...optimization,
      status,
      timestamp: Date.now(),
      baseline: this.performanceBaseline
    });
  }
  
  recordWorkflowCompletion() {
    console.log('✅ 性能优化工作流完成');
    console.log(`优化历史: ${this.optimizationHistory.length} 条记录`);
    
    // 生成最终报告
    this.generateFinalReport();
  }
}

// 性能回归检测器
class RegressionDetector {
  constructor(config = {}) {
    this.config = {
      sensitivity: 0.1,
      historySize: 100,
      checkInterval: 300000,
      ...config
    };
    
    this.history = [];
    this.alerts = [];
    this.isRunning = false;
  }
  
  start() {
    this.isRunning = true;
    this.monitoringInterval = setInterval(
      () => this.checkForRegression(),
      this.config.checkInterval
    );
  }
  
  stop() {
    this.isRunning = false;
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval);
    }
  }
  
  async checkForRegression() {
    // 收集当前性能数据
    const currentMetrics = await this.collectMetrics();
    
    if (this.history.length > 0) {
      // 与历史数据比较
      const regression = this.detectRegression(currentMetrics);
      
      if (regression) {
        // 触发告警
        this.triggerAlert(regression);
        
        // 记录回归
        this.alerts.push({
          ...regression,
          timestamp: Date.now()
        });
      }
    }
    
    // 添加到历史记录
    this.history.push({
      timestamp: Date.now(),
      metrics: currentMetrics
    });
    
    // 限制历史记录大小
    if (this.history.length > this.config.historySize) {
      this.history.shift();
    }
  }
  
  async collectMetrics() {
    const navigation = performance.getEntriesByType('navigation')[0];
    
    return {
      lcp: this.getMetricValue('largest-contentful-paint'),
      fid: this.getMetricValue('first-input-delay'),
      cls: this.getMetricValue('cumulative-layout-shift'),
      ttfb: navigation?.responseStart - navigation?.requestStart || 0,
      loadTime: navigation?.loadEventEnd - navigation?.startTime || 0,
      memory: this.getMemoryUsage()?.percentage || 0
    };
  }
  
  detectRegression(currentMetrics) {
    // 计算历史平均值
    const historicalAverage = this.calculateHistoricalAverage();
    
    // 检查每个指标
    for (const [metric, value] of Object.entries(currentMetrics)) {
      const avg = historicalAverage[metric];
      
      if (avg && value) {
        const change = (value - avg) / avg;
        
        // 如果变化超过敏感度阈值且是负向变化(性能下降)
        if (Math.abs(change) > this.config.sensitivity && value > avg) {
          return {
            metric,
            currentValue: value,
            historicalAverage: avg,
            changePercentage: change * 100,
            severity: this.calculateSeverity(change)
          };
        }
      }
    }
    
    return null;
  }
  
  calculateHistoricalAverage() {
    if (this.history.length === 0) return {};
    
    const sums = {};
    const counts = {};
    
    this.history.forEach(record => {
      Object.entries(record.metrics).forEach(([metric, value]) => {
        if (value != null) {
          sums[metric] = (sums[metric] || 0) + value;
          counts[metric] = (counts[metric] || 0) + 1;
        }
      });
    });
    
    const averages = {};
    Object.keys(sums).forEach(metric => {
      averages[metric] = sums[metric] / counts[metric];
    });
    
    return averages;
  }
  
  calculateSeverity(change) {
    const absChange = Math.abs(change);
    
    if (absChange > 0.5) return 'critical';   // 50%以上
    if (absChange > 0.3) return 'high';       // 30%以上
    if (absChange > 0.2) return 'medium';     // 20%以上
    if (absChange > 0.1) return 'low';        // 10%以上
    return 'info';
  }
  
  triggerAlert(regression) {
    const alertMessage = `
🚨 性能回归检测到!
指标: ${regression.metric}
当前值: ${regression.currentValue.toFixed(2)}
历史平均值: ${regression.historicalAverage.toFixed(2)}
变化: ${regression.changePercentage.toFixed(2)}%
严重程度: ${regression.severity}
时间: ${new Date().toLocaleString()}
    `;
    
    console.error(alertMessage);
    
    // 发送到监控系统
    this.sendAlertToMonitoring(regression);
    
    // 触发自定义事件
    window.dispatchEvent(new CustomEvent('performance-regression', {
      detail: regression
    }));
  }
  
  sendAlertToMonitoring(regression) {
    // 使用sendBeacon发送告警
    if (navigator.sendBeacon) {
      const data = JSON.stringify({
        type: 'performance_regression',
        data: regression,
        url: window.location.href,
        timestamp: Date.now()
      });
      
      navigator.sendBeacon('/api/performance-alerts', data);
    }
  }
  
  getMetricValue(metricName) {
    const entries = performance.getEntriesByName(metricName);
    return entries.length > 0 ? entries[0].startTime : null;
  }
  
  getMemoryUsage() {
    if ('memory' in performance) {
      return performance.memory;
    }
    return null;
  }
}

// 使用工作流
document.addEventListener('DOMContentLoaded', async () => {
  const workflow = new PerformanceOptimizationWorkflow();
  
  // 自动开始优化工作流
  setTimeout(async () => {
    try {
      await workflow.measurePerformance();
    } catch (error) {
      console.error('性能优化工作流失败:', error);
    }
  }, 3000); // 等待3秒让页面稳定
  
  // 手动触发按钮(用于演示)
  document.getElementById('start-optimization')?.addEventListener('click', async () => {
    await workflow.measurePerformance();
  });
});

10.2 性能预算管理与执行

javascript 复制代码
// 性能预算管理器
class PerformanceBudgetManager {
  constructor(config = {}) {
    this.budgets = this.loadBudgets(config);
    this.violations = new Map();
    this.enforcement = config.enforcement || 'warning'; // warning, error, or strict
    this.monitoringInterval = null;
    
    this.init();
  }
  
  init() {
    // 加载预算配置
    this.loadConfiguration();
    
    // 设置监控
    this.setupMonitoring();
    
    // 绑定事件
    this.bindEvents();
  }
  
  loadBudgets(config) {
    // 默认性能预算
    const defaultBudgets = {
      // Core Web Vitals
      lcp: { max: 4000, weight: 1.0 },          // Largest Contentful Paint
      fid: { max: 100, weight: 0.8 },           // First Input Delay
      cls: { max: 0.1, weight: 1.0 },           // Cumulative Layout Shift
      
      // Loading performance
      fcp: { max: 2000, weight: 0.7 },          // First Contentful Paint
      ttfb: { max: 600, weight: 0.6 },          // Time to First Byte
      load: { max: 5000, weight: 0.5 },         // Full Load Time
      
      // Resource budgets
      resources: {
        total: { max: 5 * 1024 * 1024, weight: 1.0 },      // 5MB
        js: { max: 300 * 1024, weight: 0.9 },              // 300KB
        css: { max: 100 * 1024, weight: 0.7 },             // 100KB
        images: { max: 4 * 1024 * 1024, weight: 0.8 },     // 4MB
        fonts: { max: 200 * 1024, weight: 0.6 },           // 200KB
      },
      
      // Runtime performance
      fps: { min: 50, weight: 0.7 },            // Frames Per Second
      memory: { max: 0.8, weight: 0.9 },        // 80% memory usage
      longTasks: { max: 0, weight: 1.0 },       // No long tasks
      
      // Request counts
      requests: {
        total: { max: 50, weight: 0.5 },
        thirdParty: { max: 10, weight: 0.8 },
      }
    };
    
    return { ...defaultBudgets, ...config.budgets };
  }
  
  setupMonitoring() {
    // 设置定期监控
    this.monitoringInterval = setInterval(() => {
      this.checkBudgets();
    }, 30000); // 每30秒检查一次
    
    // 页面加载完成后立即检查
    window.addEventListener('load', () => {
      setTimeout(() => this.checkBudgets(), 1000);
    });
    
    // 用户交互时检查
    document.addEventListener('click', () => {
      setTimeout(() => this.checkRuntimeBudgets(), 500);
    }, { passive: true });
  }
  
  async checkBudgets() {
    console.log('🔍 检查性能预算...');
    
    const violations = {
      loading: await this.checkLoadingBudgets(),
      resources: await this.checkResourceBudgets(),
      runtime: await this.checkRuntimeBudgets(),
      cumulative: await this.checkCumulativeBudgets()
    };
    
    // 处理违规
    this.handleViolations(violations);
    
    // 更新仪表板
    this.updateDashboard(violations);
    
    return violations;
  }
  
  async checkLoadingBudgets() {
    const violations = [];
    
    // 检查核心Web指标
    const lcp = this.getMetricValue('largest-contentful-paint');
    if (lcp && lcp > this.budgets.lcp.max) {
      violations.push(this.createViolation('lcp', lcp, this.budgets.lcp));
    }
    
    const fid = this.getMetricValue('first-input-delay');
    if (fid && fid > this.budgets.fid.max) {
      violations.push(this.createViolation('fid', fid, this.budgets.fid));
    }
    
    const cls = this.getMetricValue('cumulative-layout-shift');
    if (cls && cls > this.budgets.cls.max) {
      violations.push(this.createViolation('cls', cls, this.budgets.cls));
    }
    
    return violations;
  }
  
  async checkResourceBudgets() {
    const violations = [];
    const resources = performance.getEntriesByType('resource');
    
    // 按类型汇总资源
    const resourceSummary = resources.reduce((acc, resource) => {
      const type = resource.initiatorType;
      if (!acc[type]) {
        acc[type] = { count: 0, size: 0 };
      }
      acc[type].count++;
      acc[type].size += resource.transferSize || 0;
      return acc;
    }, {});
    
    // 检查总资源大小
    const totalSize = resources.reduce((sum, r) => sum + (r.transferSize || 0), 0);
    if (totalSize > this.budgets.resources.total.max) {
      violations.push(this.createViolation(
        'resources.total',
        totalSize,
        this.budgets.resources.total
      ));
    }
    
    // 检查各类型资源
    Object.entries(resourceSummary).forEach(([type, data]) => {
      const budgetKey = type.toLowerCase();
      const budget = this.budgets.resources[budgetKey];
      
      if (budget && data.size > budget.max) {
        violations.push(this.createViolation(
          `resources.${budgetKey}`,
          data.size,
          budget
        ));
      }
    });
    
    // 检查请求数量
    const totalRequests = resources.length;
    if (totalRequests > this.budgets.requests.total.max) {
      violations.push(this.createViolation(
        'requests.total',
        totalRequests,
        this.budgets.requests.total
      ));
    }
    
    // 检查第三方请求
    const thirdPartyRequests = resources.filter(r => {
      const url = new URL(r.name);
      return url.hostname !== window.location.hostname;
    }).length;
    
    if (thirdPartyRequests > this.budgets.requests.thirdParty.max) {
      violations.push(this.createViolation(
        'requests.thirdParty',
        thirdPartyRequests,
        this.budgets.requests.thirdParty
      ));
    }
    
    return violations;
  }
  
  async checkRuntimeBudgets() {
    const violations = [];
    
    // 检查FPS
    const fps = await this.calculateFPS();
    if (fps < this.budgets.fps.min) {
      violations.push(this.createViolation('fps', fps, this.budgets.fps));
    }
    
    // 检查内存使用
    const memory = this.getMemoryUsage();
    if (memory && memory.percentage > this.budgets.memory.max) {
      violations.push(this.createViolation('memory', memory.percentage, this.budgets.memory));
    }
    
    // 检查长任务
    const longTasks = performance.getEntriesByType('longtask');
    const longTaskCount = longTasks.filter(lt => lt.duration > 50).length;
    
    if (longTaskCount > this.budgets.longTasks.max) {
      violations.push(this.createViolation(
        'longTasks',
        longTaskCount,
        this.budgets.longTasks
      ));
    }
    
    return violations;
  }
  
  async checkCumulativeBudgets() {
    // 检查累积预算(如CLS需要在整个页面生命周期中检查)
    const violations = [];
    
    // 监听布局偏移
    if ('PerformanceObserver' in window) {
      const clsObserver = new PerformanceObserver((list) => {
        const entries = list.getEntries();
        const totalCLS = entries.reduce((sum, entry) => {
          if (!entry.hadRecentInput) {
            return sum + entry.value;
          }
          return sum;
        }, 0);
        
        if (totalCLS > this.budgets.cls.max) {
          violations.push(this.createViolation('cls_cumulative', totalCLS, this.budgets.cls));
        }
      });
      
      clsObserver.observe({ entryTypes: ['layout-shift'] });
    }
    
    return violations;
  }
  
  createViolation(metric, value, budget) {
    const violation = {
      metric,
      value,
      budget: budget.max || budget.min,
      exceededBy: budget.max ? 
        ((value - budget.max) / budget.max * 100).toFixed(1) :
        ((budget.min - value) / budget.min * 100).toFixed(1),
      weight: budget.weight || 1.0,
      timestamp: Date.now(),
      url: window.location.href
    };
    
    // 计算严重程度分数
    violation.severityScore = this.calculateSeverityScore(violation);
    
    return violation;
  }
  
  calculateSeverityScore(violation) {
    const percentage = parseFloat(violation.exceededBy);
    const weight = violation.weight;
    
    // 基础分数 = 超出百分比 * 权重
    let score = Math.abs(percentage) * weight;
    
    // 关键指标额外权重
    const criticalMetrics = ['lcp', 'cls', 'fid'];
    if (criticalMetrics.includes(violation.metric)) {
      score *= 1.5;
    }
    
    return Math.min(score, 100); // 上限100分
  }
  
  handleViolations(violations) {
    const allViolations = Object.values(violations).flat();
    
    if (allViolations.length === 0) {
      console.log('✅ 所有性能指标都在预算内');
      return;
    }
    
    // 按严重程度排序
    allViolations.sort((a, b) => b.severityScore - a.severityScore);
    
    // 记录违规
    allViolations.forEach(violation => {
      const key = `${violation.metric}_${violation.timestamp}`;
      this.violations.set(key, violation);
      
      this.logViolation(violation);
    });
    
    // 根据执行策略处理
    switch (this.enforcement) {
      case 'warning':
        this.handleAsWarning(allViolations);
        break;
      case 'error':
        this.handleAsError(allViolations);
        break;
      case 'strict':
        this.handleAsStrict(allViolations);
        break;
    }
  }
  
  handleAsWarning(violations) {
    // 警告模式:仅记录和通知
    console.warn(`⚠️ 检测到 ${violations.length} 个性能预算违规`);
    
    // 发送到监控系统
    this.reportToMonitoring(violations);
    
    // 显示用户通知(可选)
    if (violations.some(v => v.severityScore > 50)) {
      this.showUserWarning(violations);
    }
  }
  
  handleAsError(violations) {
    // 错误模式:开发环境报错
    console.error(`❌ 检测到 ${violations.length} 个性能预算违规`);
    
    // 发送到错误追踪
    this.reportToErrorTracking(violations);
    
    // 在开发环境抛出错误
    if (process.env.NODE_ENV === 'development') {
      const criticalViolations = violations.filter(v => v.severityScore > 70);
      if (criticalViolations.length > 0) {
        throw new Error(`关键性能预算违规: ${criticalViolations.map(v => v.metric).join(', ')}`);
      }
    }
  }
  
  handleAsStrict(violations) {
    // 严格模式:阻止功能或降级体验
    const criticalViolations = violations.filter(v => v.severityScore > 80);
    
    if (criticalViolations.length > 0) {
      console.error('🚨 检测到关键性能违规,启用降级模式');
      
      // 启用降级体验
      this.enableDegradedExperience();
      
      // 发送紧急警报
      this.sendCriticalAlert(criticalViolations);
    }
  }
  
  enableDegradedExperience() {
    // 降级体验策略
    console.log('启用性能降级模式');
    
    // 1. 禁用非关键功能
    document.querySelectorAll('[data-non-critical]').forEach(el => {
      el.style.display = 'none';
    });
    
    // 2. 降低图片质量
    document.querySelectorAll('img').forEach(img => {
      if (!img.hasAttribute('data-critical')) {
        img.loading = 'lazy';
        img.decoding = 'async';
      }
    });
    
    // 3. 禁用复杂动画
    document.body.classList.add('reduced-motion');
    
    // 4. 延迟加载第三方脚本
    this.deferThirdPartyScripts();
  }
  
  // 报告和监控
  reportToMonitoring(violations) {
    if (navigator.sendBeacon) {
      const data = JSON.stringify({
        type: 'performance_budget_violations',
        violations,
        timestamp: Date.now(),
        url: window.location.href,
        userAgent: navigator.userAgent
      });
      
      navigator.sendBeacon('/api/performance-violations', data);
    }
  }
  
  reportToErrorTracking(violations) {
    // 发送到错误追踪服务(如Sentry)
    if (window.Sentry) {
      violations.forEach(violation => {
        Sentry.captureMessage(`性能预算违规: ${violation.metric}`, {
          level: 'warning',
          extra: violation
        });
      });
    }
  }
  
  sendCriticalAlert(violations) {
    // 发送关键警报
    const alertData = {
      title: '关键性能预算违规',
      body: `检测到 ${violations.length} 个关键违规`,
      violations: violations.map(v => ({
        metric: v.metric,
        exceededBy: v.exceededBy,
        score: v.severityScore
      })),
      timestamp: Date.now()
    };
    
    // 发送到监控系统
    fetch('/api/critical-alerts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(alertData),
      keepalive: true // 确保在页面卸载时也能发送
    });
  }
  
  // 辅助方法
  getMetricValue(metricName) {
    const entries = performance.getEntriesByName(metricName);
    return entries.length > 0 ? entries[0].startTime : null;
  }
  
  async calculateFPS() {
    return new Promise(resolve => {
      let frames = 0;
      let start = performance.now();
      
      const count = () => {
        frames++;
        const now = performance.now();
        
        if (now - start >= 1000) {
          resolve(frames);
        } else {
          requestAnimationFrame(count);
        }
      };
      
      requestAnimationFrame(count);
    });
  }
  
  getMemoryUsage() {
    if ('memory' in performance) {
      const mem = performance.memory;
      return {
        used: mem.usedJSHeapSize,
        total: mem.totalJSHeapSize,
        limit: mem.jsHeapSizeLimit,
        percentage: mem.usedJSHeapSize / mem.jsHeapSizeLimit
      };
    }
    return null;
  }
  
  // 仪表板更新
  updateDashboard(violations) {
    // 更新实时仪表板
    const dashboard = document.getElementById('performance-dashboard');
    if (dashboard) {
      this.renderDashboard(dashboard, violations);
    }
  }
  
  renderDashboard(container, violations) {
    const allViolations = Object.values(violations).flat();
    const totalScore = allViolations.reduce((sum, v) => sum + v.severityScore, 0);
    
    container.innerHTML = `
      <div class="performance-dashboard">
        <h3>性能预算监控</h3>
        <div class="score ${totalScore > 50 ? 'warning' : totalScore > 20 ? 'caution' : 'good'}">
          风险评分: ${totalScore.toFixed(1)}
        </div>
        ${allViolations.length > 0 ? `
          <div class="violations">
            <h4>违规项 (${allViolations.length})</h4>
            <ul>
              ${allViolations.map(v => ` <li class="severity-${Math.floor(v.severityScore / 20)}"> ${v.metric}: ${v.value.toFixed(1)} (预算: ${v.budget}, 超出: ${v.exceededBy}%) </li> `).join('')}
            </ul>
          </div>
        ` : '<div class="all-good">✅ 所有指标都在预算内</div>'}
      </div>
    `;
  }
  
  // 配置管理
  loadConfiguration() {
    try {
      const saved = localStorage.getItem('performance-budget-config');
      if (saved) {
        const config = JSON.parse(saved);
        this.budgets = { ...this.budgets, ...config.budgets };
        this.enforcement = config.enforcement || this.enforcement;
      }
    } catch (error) {
      console.warn('加载性能预算配置失败:', error);
    }
  }
  
  saveConfiguration() {
    try {
      const config = {
        budgets: this.budgets,
        enforcement: this.enforcement,
        timestamp: Date.now()
      };
      
      localStorage.setItem('performance-budget-config', JSON.stringify(config));
    } catch (error) {
      console.warn('保存性能预算配置失败:', error);
    }
  }
  
  // 清理
  destroy() {
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval);
    }
    
    // 保存最终状态
    this.saveConfiguration();
  }
}

// 使用性能预算管理器
document.addEventListener('DOMContentLoaded', () => {
  // 初始化性能预算管理器
  const budgetManager = new PerformanceBudgetManager({
    enforcement: process.env.NODE_ENV === 'production' ? 'warning' : 'error',
    budgets: {
      // 可以根据项目需求覆盖默认预算
      lcp: { max: 2500, weight: 1.0 }, // 更严格的LCP预算
      resources: {
        js: { max: 200 * 1024, weight: 0.9 }, // 200KB JS预算
        images: { max: 2 * 1024 * 1024, weight: 0.8 } // 2MB图片预算
      }
    }
  });
  
  // 页面卸载前保存数据
  window.addEventListener('beforeunload', () => {
    budgetManager.destroy();
  });
  
  // 开发工具:手动检查预算
  if (process.env.NODE_ENV === 'development') {
    window.checkPerformanceBudget = () => budgetManager.checkBudgets();
    
    // 添加性能预算面板到页面
    const panel = document.createElement('div');
    panel.id = 'performance-dashboard';
    panel.style.cssText = `
      position: fixed;
      top: 10px;
      right: 10px;
      background: white;
      border: 1px solid #ddd;
      padding: 10px;
      border-radius: 5px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
      z-index: 10000;
      max-width: 300px;
      font-family: monospace;
      font-size: 12px;
    `;
    
    document.body.appendChild(panel);
  }
});

实施建议

  1. 高回报优化

    • ✅ 启用Gzip/Brotli压缩
    • ✅ 优化图片(转换为WebP/AVIF格式)
    • ✅ 配置合理的缓存策略
    • ✅ 使用CDN加速静态资源
  2. 中期优化

    • 🔄 实施代码分割和懒加载
    • 🔄 优化关键渲染路径
    • 🔄 减少JavaScript包体积
    • 🔄 实现虚拟列表处理大数据
  3. 长期维护

    • 📊 建立性能监控体系
    • 📊 设置性能预算和CI检查
    • 📊 定期进行性能审计
    • 📊 建立性能文化(团队重视性能)
相关推荐
TOYOAUTOMATON2 小时前
GTH系列模组介绍
前端·目标检测·自动化
Array*2 小时前
java实现word中插入附件(支持所有文件格式)
java·开发语言·word·poi·ole
2022.11.7始学前端2 小时前
n8n第十节 把Markdown格式的会议纪要发到企微
前端·chrome·n8n
Donald_brian2 小时前
线程同步
java·开发语言·jvm
全栈陈序员2 小时前
【Python】基础语法入门(十五)——标准库精选:提升效率的内置工具箱
开发语言·人工智能·python·学习
阿蒙Amon2 小时前
JavaScript学习笔记:4.循环与迭代
javascript·笔记·学习
爱倒腾的老唐2 小时前
02、打不开某个网站
windows·笔记·电脑
爱上妖精的尾巴2 小时前
6-3 WPS JS宏 add、delete、size、clear集合成员添加与删除
javascript·wps·js宏·jsa
郑州光合科技余经理2 小时前
技术视角:海外版一站式同城生活服务平台源码解析
java·开发语言·uni-app·php·排序算法·objective-c·生活