轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • JavaScript
  • TypeScript
  • Node.js
  • Vue.js
  • 前端工程化
  • 浏览器与Web API
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • JavaScript
  • TypeScript
  • Node.js
  • Vue.js
  • 前端工程化
  • 浏览器与Web API
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

  • TypeScript

  • Node.js

  • Vue.js

  • 工程化

  • 浏览器与Web API

    • HTML基础-语义化标签与文档结构
    • HTML基础-文本与排版标签
    • HTML基础-列表与表格
    • HTML表单-Input类型详解
    • HTML表单-表单元素与验证
    • HTML交互-多媒体元素
    • HTML工程化-模板与组件化
    • HTML工程化-性能优化
      • 一、关键渲染路径优化
        • 1.1 理解关键渲染路径
        • 1.2 优化HTML结构
        • 1.3 CSS优化策略
      • 二、资源加载优化
        • 2.1 资源提示(Resource Hints)
        • 2.2 脚本加载策略
        • 2.3 图片优化
        • 2.4 字体优化
      • 三、HTML结构优化
        • 3.1 减少DOM深度
        • 3.2 避免不必要的包装元素
        • 3.3 使用语义化标签
      • 四、缓存策略
        • 4.1 HTTP缓存头
        • 4.2 Service Worker缓存
      • 五、渲染性能优化
        • 5.1 避免布局偏移(CLS)
        • 5.2 优化首次内容绘制(FCP)
        • 5.3 减少主线程工作
      • 六、网络优化
        • 6.1 HTTP/2优化
        • 6.2 减少请求大小
        • 6.3 减少第三方脚本影响
      • 七、性能监控
        • 7.1 Performance API
        • 7.2 Web Vitals
        • 7.3 PerformanceObserver
      • 八、综合示例
      • 九、最佳实践
        • 9.1 性能优化检查清单
        • 9.2 性能预算
        • 9.3 渐进式增强
      • 十、性能测试工具
        • 10.1 浏览器开发工具
        • 10.2 在线工具
        • 10.3 性能监控服务
      • 十一、总结
    • CSS基础-选择器与优先级
    • CSS基础-盒模型与布局基础
    • CSS基础-单位与颜色系统
    • CSS基础-文本与字体
    • CSS基础-背景、列表与表格样式
    • CSS布局-Flexbox完全指南
    • CSS布局-Grid网格布局
    • CSS布局-响应式设计实践
    • CSS进阶-动画与过渡
    • CSS进阶-渐变与阴影效果
    • CSS进阶-Transform与3D变换
    • CSS进阶-滤镜与混合模式
    • 现代CSS-CSS预处理器对比
    • 现代CSS-CSS-in-JS方案
    • 现代CSS-原子化CSS与Tailwind
    • CSS工程化-架构与规范
    • CSS工程化-性能优化
    • CSS工程化-PostCSS实战指南
    • Web API基础-DOM操作完全指南
    • Web API基础-事件处理与委托
    • Web API基础-BOM与浏览器环境
    • Web API存储-客户端存储方案
    • Web API网络-HTTP请求详解
    • Web API网络-实时通信方案
    • Web API交互-用户体验增强
    • HTML&CSS历代版本新特性
  • 前端
  • 浏览器与Web API
轩辕李
2019-07-12
目录

HTML工程化-性能优化

# HTML工程化-性能优化

网页性能直接影响用户体验和业务指标。本文将深入探讨HTML层面的性能优化策略,涵盖资源加载、渲染优化、缓存策略等多个方面。

# 一、关键渲染路径优化

# 1.1 理解关键渲染路径

浏览器渲染页面的关键步骤:

  1. 构建DOM树(解析HTML)
  2. 构建CSSOM树(解析CSS)
  3. 执行JavaScript
  4. 合成渲染树
  5. 布局(Layout)
  6. 绘制(Paint)
  7. 合成(Composite)

# 1.2 优化HTML结构

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <!-- 关键CSS内联 -->
  <style>
    /* 首屏关键样式 */
    body { margin: 0; font-family: sans-serif; }
    .header { background: #333; color: white; padding: 20px; }
  </style>
  
  <!-- 预连接到重要域名 -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="dns-prefetch" href="https://api.example.com">
  
  <!-- 非关键CSS异步加载 -->
  <link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/styles/main.css"></noscript>
  
  <title>性能优化示例</title>
</head>
<body>
  <!-- 首屏内容 -->
  <header class="header">
    <h1>网站标题</h1>
  </header>
  
  <!-- JavaScript放在底部,避免阻塞渲染 -->
  <script src="/scripts/app.js" defer></script>
</body>
</html>

# 1.3 CSS优化策略

<!-- 1. 关键CSS内联 -->
<style>
  /* 首屏必需的样式直接内联 */
  .hero { min-height: 100vh; }
</style>

<!-- 2. 媒体查询分离 -->
<link rel="stylesheet" href="print.css" media="print">
<link rel="stylesheet" href="mobile.css" media="(max-width: 768px)">

<!-- 3. 非关键CSS延迟加载 -->
<link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'">

# 二、资源加载优化

# 2.1 资源提示(Resource Hints)

<!-- DNS预解析 -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com">

<!-- 预连接 -->
<link rel="preconnect" href="https://cdn.example.com" crossorigin>

<!-- 预加载关键资源 -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/images/hero.jpg" as="image">
<link rel="preload" href="/scripts/critical.js" as="script">

<!-- 预获取下一页资源 -->
<link rel="prefetch" href="/page2.html">
<link rel="prefetch" href="/images/page2-hero.jpg">

<!-- 预渲染下一页 -->
<link rel="prerender" href="/next-page.html">

各种资源提示的使用场景:

  • dns-prefetch:提前解析DNS,适用于第三方域名
  • preconnect:提前建立连接(DNS + TCP + TLS),适用于关键第三方资源
  • preload:高优先级加载当前页面必需资源
  • prefetch:低优先级预取下一页资源
  • prerender:预渲染整个页面(慎用,消耗资源较多)

# 2.2 脚本加载策略

<!-- 1. 默认:阻塞HTML解析 -->
<script src="blocking.js"></script>

<!-- 2. async:异步下载,下载完立即执行 -->
<script src="analytics.js" async></script>

<!-- 3. defer:异步下载,HTML解析完后按顺序执行 -->
<script src="app.js" defer></script>
<script src="utils.js" defer></script>

<!-- 4. 模块脚本:默认defer行为 -->
<script type="module" src="main.mjs"></script>

<!-- 5. 动态加载 -->
<script>
  // 条件加载
  if (shouldLoadFeature) {
    const script = document.createElement('script');
    script.src = 'feature.js';
    script.async = true;
    document.head.appendChild(script);
  }
</script>

<!-- 6. 内联关键脚本 -->
<script>
  // 首屏必需的小段代码可以内联
  console.log('页面开始加载');
</script>

脚本加载时机对比:

属性 下载时机 执行时机 阻塞解析 执行顺序
无 立即 立即 是 按出现顺序
async 异步 下载完成后 否 不保证
defer 异步 DOMContentLoaded前 否 按出现顺序
type="module" 异步 DOMContentLoaded前 否 按依赖顺序

# 2.3 图片优化

<!-- 1. 响应式图片 -->
<img 
  src="small.jpg"
  srcset="small.jpg 300w, medium.jpg 600w, large.jpg 1200w"
  sizes="(max-width: 600px) 300px, (max-width: 1200px) 600px, 1200px"
  alt="响应式图片"
  loading="lazy"
>

<!-- 2. 现代图片格式 -->
<picture>
  <source type="image/avif" srcset="image.avif">
  <source type="image/webp" srcset="image.webp">
  <img src="image.jpg" alt="回退图片">
</picture>

<!-- 3. 懒加载 -->
<img src="placeholder.jpg" data-src="actual.jpg" loading="lazy" alt="懒加载图片">

<!-- 4. 关键图片预加载 -->
<link rel="preload" as="image" href="hero.jpg">

<!-- 5. 避免布局偏移 -->
<img 
  src="image.jpg" 
  width="800" 
  height="600" 
  alt="指定尺寸的图片"
  style="max-width: 100%; height: auto;"
>

# 2.4 字体优化

<head>
  <!-- 1. 预连接字体服务 -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  
  <!-- 2. 预加载关键字体 -->
  <link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
  
  <!-- 3. 使用font-display控制渲染 -->
  <style>
    @font-face {
      font-family: 'CustomFont';
      src: url('/fonts/custom.woff2') format('woff2');
      font-display: swap; /* 或 fallback, optional */
    }
  </style>
</head>

font-display值说明:

  • auto:浏览器默认行为
  • block:最多3秒阻塞期,然后swap
  • swap:立即使用备用字体,加载完成后替换
  • fallback:极短阻塞期(~100ms),3秒内可swap
  • optional:极短阻塞期,之后不再swap

# 三、HTML结构优化

# 3.1 减少DOM深度

<!-- ✗ 避免:过深的嵌套 -->
<div>
  <div>
    <div>
      <div>
        <div>
          <p>内容</p>
        </div>
      </div>
    </div>
  </div>
</div>

<!-- ✓ 推荐:扁平化结构 -->
<div>
  <p>内容</p>
</div>

# 3.2 避免不必要的包装元素

<!-- ✗ 避免 -->
<div class="wrapper">
  <div class="container">
    <div class="content">
      <p>文本</p>
    </div>
  </div>
</div>

<!-- ✓ 推荐 -->
<div class="content">
  <p>文本</p>
</div>

# 3.3 使用语义化标签

<!-- ✗ 避免:过度使用div -->
<div class="header">
  <div class="nav">
    <div class="nav-item">首页</div>
  </div>
</div>

<!-- ✓ 推荐:语义化标签更简洁 -->
<header>
  <nav>
    <a href="/">首页</a>
  </nav>
</header>

# 四、缓存策略

# 4.1 HTTP缓存头

<!-- 通过meta标签设置缓存(兼容性有限,建议服务端设置) -->
<meta http-equiv="Cache-Control" content="max-age=31536000">

<!-- 服务端设置示例(需在服务器配置) -->
<!-- 
  静态资源:
  Cache-Control: public, max-age=31536000, immutable
  
  HTML文档:
  Cache-Control: no-cache
  ETag: "abc123"
-->

# 4.2 Service Worker缓存

<script>
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => {
        console.log('Service Worker注册成功');
      })
      .catch(error => {
        console.error('Service Worker注册失败:', error);
      });
  }
</script>

sw.js示例:

const CACHE_NAME = 'v1';
const urlsToCache = [
  '/',
  '/styles/main.css',
  '/scripts/app.js'
];

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

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => response || fetch(event.request))
  );
});

# 五、渲染性能优化

# 5.1 避免布局偏移(CLS)

<!-- 1. 图片指定尺寸 -->
<img src="image.jpg" width="800" height="600" alt="图片">

<!-- 2. 使用aspect-ratio -->
<img src="image.jpg" alt="图片" style="aspect-ratio: 16/9; width: 100%;">

<!-- 3. 广告位预留空间 -->
<div class="ad-container" style="min-height: 250px;">
  <!-- 广告内容 -->
</div>

<!-- 4. 字体回退尺寸匹配 -->
<style>
  @font-face {
    font-family: 'CustomFont';
    src: url('custom.woff2');
    size-adjust: 100%; /* 调整回退字体尺寸 */
  }
</style>

# 5.2 优化首次内容绘制(FCP)

<!DOCTYPE html>
<html>
<head>
  <!-- 内联关键CSS -->
  <style>
    body { margin: 0; font-family: sans-serif; }
    .hero { background: #f0f0f0; padding: 60px 20px; }
  </style>
  
  <!-- 预加载关键资源 -->
  <link rel="preload" href="hero.jpg" as="image">
</head>
<body>
  <!-- 首屏内容优先 -->
  <div class="hero">
    <h1>欢迎访问</h1>
  </div>
  
  <!-- 非关键内容延迟加载 -->
  <script defer src="non-critical.js"></script>
</body>
</html>

# 5.3 减少主线程工作

<!-- 1. 使用Web Workers处理密集计算 -->
<script>
  const worker = new Worker('heavy-computation.js');
  worker.postMessage({ data: largeDataset });
  worker.onmessage = (e) => {
    console.log('计算结果:', e.data);
  };
</script>

<!-- 2. 使用requestIdleCallback -->
<script>
  function performNonCriticalWork() {
    // 非关键任务
  }

  if ('requestIdleCallback' in window) {
    requestIdleCallback(performNonCriticalWork);
  } else {
    setTimeout(performNonCriticalWork, 1);
  }
</script>

<!-- 3. 分块处理大任务 -->
<script>
  function processLargeArray(items) {
    const chunk = items.splice(0, 100);
    
    // 处理当前块
    chunk.forEach(item => process(item));
    
    if (items.length > 0) {
      requestAnimationFrame(() => processLargeArray(items));
    }
  }
</script>

# 六、网络优化

# 6.1 HTTP/2优化

<!-- HTTP/2支持多路复用,无需合并文件 -->
<link rel="stylesheet" href="header.css">
<link rel="stylesheet" href="content.css">
<link rel="stylesheet" href="footer.css">

<!-- HTTP/2服务端推送(需服务器配置) -->
<!-- Link: </styles/critical.css>; rel=preload; as=style -->

# 6.2 减少请求大小

<!-- 1. 压缩HTML(生产环境) -->
<!-- 移除空格、注释等 -->

<!-- 2. 使用CDN -->
<script src="https://cdn.example.com/library.min.js"></script>

<!-- 3. 启用gzip/brotli压缩(服务器配置) -->

<!-- 4. 代码分割 -->
<script type="module">
  // 动态导入
  document.getElementById('btn').addEventListener('click', async () => {
    const module = await import('./feature.js');
    module.init();
  });
</script>

# 6.3 减少第三方脚本影响

<!-- 1. 异步加载第三方脚本 -->
<script async src="https://www.google-analytics.com/analytics.js"></script>

<!-- 2. 延迟加载非关键第三方脚本 -->
<script>
  window.addEventListener('load', () => {
    // 页面加载完成后再加载第三方脚本
    const script = document.createElement('script');
    script.src = 'https://third-party.com/widget.js';
    document.body.appendChild(script);
  });
</script>

<!-- 3. 使用iframe隔离 -->
<iframe 
  src="third-party-widget.html" 
  loading="lazy"
  sandbox="allow-scripts"
  title="第三方小部件"
></iframe>

# 七、性能监控

# 7.1 Performance API

<script>
  window.addEventListener('load', () => {
    const perfData = window.performance.timing;
    const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
    const connectTime = perfData.responseEnd - perfData.requestStart;
    const renderTime = perfData.domComplete - perfData.domLoading;
    
    console.log('页面加载时间:', pageLoadTime);
    console.log('请求响应时间:', connectTime);
    console.log('DOM渲染时间:', renderTime);
  });
</script>

# 7.2 Web Vitals

<script type="module">
  import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'https://unpkg.com/web-vitals?module';

  getCLS(console.log); // Cumulative Layout Shift
  getFID(console.log); // First Input Delay
  getFCP(console.log); // First Contentful Paint
  getLCP(console.log); // Largest Contentful Paint
  getTTFB(console.log); // Time to First Byte
</script>

# 7.3 PerformanceObserver

<script>
  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      console.log('资源:', entry.name);
      console.log('加载时间:', entry.duration);
    }
  });

  observer.observe({ entryTypes: ['resource', 'navigation', 'paint'] });
</script>

# 八、综合示例

<html>
  <div id="perf-demo-q1r2s">
    <h3>HTML性能优化综合示例</h3>
    
    <div class="tabs-q1r2s">
      <button class="tab-btn-q1r2s active-q1r2s" data-tab="lazy-load">图片懒加载</button>
      <button class="tab-btn-q1r2s" data-tab="resource-hints">资源提示</button>
      <button class="tab-btn-q1r2s" data-tab="metrics">性能指标</button>
    </div>
    
    <div class="tab-content-q1r2s">
      <div id="lazy-load-q1r2s" class="tab-panel-q1r2s active-q1r2s">
        <h4>图片懒加载演示</h4>
        <p>滚动查看图片加载效果(使用占位符模拟):</p>
        <div class="image-list-q1r2s">
          <div class="image-item-q1r2s" data-index="1">
            <div class="placeholder-q1r2s">图片 1 加载中...</div>
          </div>
          <div class="image-item-q1r2s" data-index="2">
            <div class="placeholder-q1r2s">图片 2 加载中...</div>
          </div>
          <div class="image-item-q1r2s" data-index="3">
            <div class="placeholder-q1r2s">图片 3 加载中...</div>
          </div>
          <div class="image-item-q1r2s" data-index="4">
            <div class="placeholder-q1r2s">图片 4 加载中...</div>
          </div>
        </div>
      </div>
      
      <div id="resource-hints-q1r2s" class="tab-panel-q1r2s">
        <h4>资源提示示例</h4>
        <div class="hint-item-q1r2s">
          <code>&lt;link rel="dns-prefetch" href="https://api.example.com"&gt;</code>
          <p>DNS预解析:提前解析域名</p>
        </div>
        <div class="hint-item-q1r2s">
          <code>&lt;link rel="preconnect" href="https://cdn.example.com"&gt;</code>
          <p>预连接:建立完整连接(DNS + TCP + TLS)</p>
        </div>
        <div class="hint-item-q1r2s">
          <code>&lt;link rel="preload" href="critical.css" as="style"&gt;</code>
          <p>预加载:高优先级加载关键资源</p>
        </div>
        <div class="hint-item-q1r2s">
          <code>&lt;link rel="prefetch" href="next-page.html"&gt;</code>
          <p>预获取:低优先级预取下一页资源</p>
        </div>
        
        <button id="test-preload-q1r2s" class="btn-q1r2s">测试动态preload</button>
        <div id="preload-status-q1r2s" class="status-q1r2s"></div>
      </div>
      
      <div id="metrics-q1r2s" class="tab-panel-q1r2s">
        <h4>性能指标监控</h4>
        <div class="metrics-grid-q1r2s">
          <div class="metric-card-q1r2s">
            <h5>页面加载时间</h5>
            <p class="metric-value-q1r2s" id="load-time-q1r2s">-</p>
          </div>
          <div class="metric-card-q1r2s">
            <h5>DOM构建时间</h5>
            <p class="metric-value-q1r2s" id="dom-time-q1r2s">-</p>
          </div>
          <div class="metric-card-q1r2s">
            <h5>资源加载时间</h5>
            <p class="metric-value-q1r2s" id="resource-time-q1r2s">-</p>
          </div>
          <div class="metric-card-q1r2s">
            <h5>首次绘制时间</h5>
            <p class="metric-value-q1r2s" id="fcp-time-q1r2s">-</p>
          </div>
        </div>
        <button id="refresh-metrics-q1r2s" class="btn-q1r2s">刷新指标</button>
      </div>
    </div>
  </div>
</html>

<style>
  #perf-demo-q1r2s {
    font-family: sans-serif;
    max-width: 800px;
  }
  
  .tabs-q1r2s {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
    border-bottom: 2px solid #e0e0e0;
  }
  
  .tab-btn-q1r2s {
    padding: 12px 20px;
    border: none;
    background: transparent;
    cursor: pointer;
    font-size: 14px;
    color: #666;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    transition: all 0.2s;
  }
  
  .tab-btn-q1r2s:hover {
    color: #2196F3;
  }
  
  .tab-btn-q1r2s.active-q1r2s {
    color: #2196F3;
    border-bottom-color: #2196F3;
  }
  
  .tab-panel-q1r2s {
    display: none;
    padding: 20px;
    background: #f9f9f9;
    border-radius: 8px;
  }
  
  .tab-panel-q1r2s.active-q1r2s {
    display: block;
  }
  
  .image-list-q1r2s {
    max-height: 300px;
    overflow-y: auto;
    border: 1px solid #ddd;
    padding: 12px;
    background: white;
  }
  
  .image-item-q1r2s {
    margin-bottom: 16px;
    border: 1px solid #e0e0e0;
    border-radius: 4px;
    overflow: hidden;
  }
  
  .placeholder-q1r2s {
    height: 150px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading-q1r2s 1.5s infinite;
    color: #999;
    font-size: 14px;
  }
  
  @keyframes loading-q1r2s {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
  }
  
  .loaded-q1r2s {
    height: 150px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    font-size: 24px;
    font-weight: bold;
  }
  
  .hint-item-q1r2s {
    margin-bottom: 16px;
    padding: 12px;
    background: white;
    border-left: 3px solid #2196F3;
  }
  
  .hint-item-q1r2s code {
    display: block;
    background: #f5f5f5;
    padding: 8px;
    border-radius: 4px;
    font-size: 12px;
    margin-bottom: 8px;
    overflow-x: auto;
  }
  
  .hint-item-q1r2s p {
    margin: 0;
    color: #666;
    font-size: 14px;
  }
  
  .btn-q1r2s {
    background: #2196F3;
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    margin-top: 12px;
    transition: background 0.2s;
  }
  
  .btn-q1r2s:hover {
    background: #1976D2;
  }
  
  .status-q1r2s {
    margin-top: 12px;
    padding: 12px;
    background: white;
    border-radius: 4px;
    min-height: 20px;
  }
  
  .metrics-grid-q1r2s {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
    margin-bottom: 16px;
  }
  
  .metric-card-q1r2s {
    background: white;
    padding: 16px;
    border-radius: 8px;
    text-align: center;
    border: 1px solid #e0e0e0;
  }
  
  .metric-card-q1r2s h5 {
    margin: 0 0 8px 0;
    font-size: 13px;
    color: #666;
    font-weight: normal;
  }
  
  .metric-value-q1r2s {
    margin: 0;
    font-size: 24px;
    font-weight: bold;
    color: #2196F3;
  }
</style>

<script>
  const tabBtns = document.querySelectorAll('.tab-btn-q1r2s');
  const tabPanels = document.querySelectorAll('.tab-panel-q1r2s');
  
  tabBtns.forEach(btn => {
    btn.addEventListener('click', () => {
      const targetTab = btn.dataset.tab;
      
      tabBtns.forEach(b => b.classList.remove('active-q1r2s'));
      tabPanels.forEach(p => p.classList.remove('active-q1r2s'));
      
      btn.classList.add('active-q1r2s');
      document.getElementById(`${targetTab}-q1r2s`).classList.add('active-q1r2s');
    });
  });

  const imageList = document.querySelector('.image-list-q1r2s');
  const imageItems = document.querySelectorAll('.image-item-q1r2s');
  
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const item = entry.target;
        const index = item.dataset.index;
        
        setTimeout(() => {
          const placeholder = item.querySelector('.placeholder-q1r2s');
          placeholder.className = 'loaded-q1r2s';
          placeholder.textContent = `图片 ${index}`;
        }, 500);
        
        observer.unobserve(item);
      }
    });
  }, { threshold: 0.1 });
  
  imageItems.forEach(item => observer.observe(item));

  document.getElementById('test-preload-q1r2s').addEventListener('click', () => {
    const statusEl = document.getElementById('preload-status-q1r2s');
    statusEl.innerHTML = '<p style="color: #2196F3;">正在动态创建preload链接...</p>';
    
    const link = document.createElement('link');
    link.rel = 'preload';
    link.as = 'image';
    link.href = 'https://via.placeholder.com/400x300';
    
    link.onload = () => {
      statusEl.innerHTML = '<p style="color: #4caf50;">✓ 资源预加载成功!</p>';
    };
    
    link.onerror = () => {
      statusEl.innerHTML = '<p style="color: #f44336;">✗ 资源预加载失败</p>';
    };
    
    document.head.appendChild(link);
  });

  function updateMetrics() {
    if (window.performance && window.performance.timing) {
      const timing = window.performance.timing;
      
      const loadTime = timing.loadEventEnd - timing.navigationStart;
      const domTime = timing.domComplete - timing.domLoading;
      const resourceTime = timing.responseEnd - timing.requestStart;
      
      document.getElementById('load-time-q1r2s').textContent = loadTime > 0 ? `${loadTime}ms` : '计算中...';
      document.getElementById('dom-time-q1r2s').textContent = domTime > 0 ? `${domTime}ms` : '计算中...';
      document.getElementById('resource-time-q1r2s').textContent = resourceTime > 0 ? `${resourceTime}ms` : '计算中...';
      
      if (window.performance.getEntriesByType) {
        const paintEntries = window.performance.getEntriesByType('paint');
        const fcp = paintEntries.find(entry => entry.name === 'first-contentful-paint');
        
        if (fcp) {
          document.getElementById('fcp-time-q1r2s').textContent = `${Math.round(fcp.startTime)}ms`;
        } else {
          document.getElementById('fcp-time-q1r2s').textContent = '不支持';
        }
      }
    }
  }

  updateMetrics();
  
  window.addEventListener('load', () => {
    setTimeout(updateMetrics, 100);
  });
  
  document.getElementById('refresh-metrics-q1r2s').addEventListener('click', updateMetrics);
</script>

# 九、最佳实践

# 9.1 性能优化检查清单

HTML结构:

  • ✓ 减少DOM深度和节点数量
  • ✓ 使用语义化标签
  • ✓ 避免不必要的包装元素

资源加载:

  • ✓ 关键CSS内联
  • ✓ 非关键CSS异步加载
  • ✓ JavaScript使用defer或async
  • ✓ 图片使用loading="lazy"
  • ✓ 合理使用资源提示(preload、prefetch等)

图片优化:

  • ✓ 使用响应式图片
  • ✓ 提供现代格式(WebP、AVIF)
  • ✓ 指定图片尺寸,避免布局偏移
  • ✓ 懒加载非首屏图片

字体优化:

  • ✓ 预加载关键字体
  • ✓ 使用font-display控制渲染
  • ✓ 限制字体变体数量

缓存策略:

  • ✓ 设置合理的缓存头
  • ✓ 使用Service Worker
  • ✓ 静态资源使用内容哈希命名

性能监控:

  • ✓ 监控Core Web Vitals
  • ✓ 使用Performance API
  • ✓ 定期进行性能审计

# 9.2 性能预算

设置明确的性能目标:

const performanceBudget = {
  'bundle.js': 200, // KB
  'styles.css': 50,
  'total-page-size': 500,
  'first-contentful-paint': 1500, // ms
  'largest-contentful-paint': 2500,
  'time-to-interactive': 3500,
  'cumulative-layout-shift': 0.1,
  'first-input-delay': 100
};

# 9.3 渐进式增强

<!-- 基础HTML保证内容可访问 -->
<div class="feature">
  <h3>功能标题</h3>
  <p>功能描述</p>
</div>

<!-- 增强样式 -->
<style>
  @supports (display: grid) {
    .feature {
      display: grid;
      gap: 20px;
    }
  }
</style>

<!-- 增强交互 -->
<script>
  if ('IntersectionObserver' in window) {
    // 使用现代API
  } else {
    // 降级方案
  }
</script>

# 十、性能测试工具

# 10.1 浏览器开发工具

  • Chrome DevTools:Lighthouse、Performance面板、Network面板
  • Firefox Developer Tools:性能分析工具
  • Safari Web Inspector:时间轴和网络工具

# 10.2 在线工具

  • PageSpeed Insights:Google的页面速度分析工具
  • WebPageTest:详细的性能测试报告
  • GTmetrix:综合性能分析
  • Lighthouse CI:持续集成中的性能监控

# 10.3 性能监控服务

  • Google Analytics:用户体验监控
  • New Relic:应用性能监控
  • Sentry:性能和错误追踪
  • SpeedCurve:持续性能监控

# 十一、总结

HTML性能优化是一个系统工程,涉及多个方面:

  1. 关键渲染路径优化:减少阻塞,加快首屏渲染
  2. 资源加载优化:合理使用资源提示,优化加载策略
  3. HTML结构优化:简化DOM,使用语义化标签
  4. 缓存策略:充分利用浏览器和服务端缓存
  5. 渲染性能优化:避免布局偏移,减少主线程工作
  6. 网络优化:减少请求数量和大小
  7. 性能监控:持续跟踪Core Web Vitals

性能优化需要在开发过程中持续关注,通过工具监控、定期审计和优化,才能为用户提供流畅的体验。

祝你变得更强!

编辑 (opens new window)
#HTML#工程化#性能优化#Web性能#加载优化
上次更新: 2025/11/28
HTML工程化-模板与组件化
CSS基础-选择器与优先级

← HTML工程化-模板与组件化 CSS基础-选择器与优先级→

最近更新
01
AI编程时代的一些心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code 最佳实践(个人版)
08-01
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式