0%

性能与优化

性能与优化

Web 性能优化是现代前端开发的核心关注点。快速加载的页面不仅能提升用户体验,还能提高搜索引擎排名和转化率。本文档将介绍 HTML 层面的性能优化技术,包括资源加载策略、图片与字体优化、关键渲染路径优化等内容。

1. 资源加载优化

1.1 JavaScript 加载策略

1.1.1 <script> 标签的加载方式

默认情况下,<script> 标签会阻塞 HTML 解析,导致页面渲染延迟。

无属性(阻塞渲染)

1
<script src="app.js"></script>

使用 defer 属性(延迟执行)

1
<script defer src="app.js"></script>
  • 立即下载,但不阻塞 HTML 解析
  • 在 DOM 解析完成后,按顺序执行
  • 适用于需要操作 DOM 的脚本
  • 多个 defer 脚本按顺序执行

使用 async 属性(异步执行)

1
<script async src="analytics.js"></script>
  • 立即下载,不阻塞 HTML 解析
  • 下载完成后立即执行(可能中断 HTML 解析)
  • 执行顺序不确定
  • 适用于独立的第三方脚本(如分析工具)

1.1.2 defer vs async 对比

特性 无属性 defer async
阻塞 HTML 解析 ✅ 是 ❌ 否 ❌ 否
执行时机 立即 DOM 解析后 下载后立即
执行顺序 按顺序 按顺序 不确定
适用场景 必要脚本 依赖 DOM 的脚本 独立脚本

推荐实践

1
2
3
4
5
<!-- 关键脚本使用 defer -->
<script defer src="main.js"></script>

<!-- 第三方分析工具使用 async -->
<script async src="https://www.google-analytics.com/analytics.js"></script>

1.2 预加载(Preload)

preload 用于提前加载当前页面必需的关键资源。

1.2.1 基本语法

1
2
3
4
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="hero-image.jpg" as="image">
<link rel="preload" href="app.js" as="script">

1.2.2 as 属性值

as 值 说明 示例
script JavaScript 文件 app.js
style CSS 样式表 main.css
image 图片资源 hero.jpg
font 字体文件 font.woff2
audio 音频文件 sound.mp3
video 视频文件 video.mp4
fetch Fetch/XMLHttpRequest API 数据
document HTML 文档 page.html

1.2.3 预加载关键资源示例

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

<!-- 预加载关键 CSS -->
<link rel="preload"
href="styles/critical.css"
as="style">

<!-- 预加载首屏图片 -->
<link rel="preload"
href="images/hero.jpg"
as="image">

<!-- 预加载关键脚本 -->
<link rel="preload"
href="js/main.js"
as="script">
</head>

注意事项

  • 仅预加载当前页面必需的关键资源
  • 字体文件必须设置 crossorigin 属性
  • 避免预加载过多资源,导致带宽浪费

1.3 预获取(Prefetch)

prefetch 用于在浏览器空闲时预加载用户可能访问的下一个页面的资源。

1.3.1 基本语法

1
2
3
<link rel="prefetch" href="next-page.html">
<link rel="prefetch" href="next-page.js">
<link rel="prefetch" href="next-page.css">

1.3.2 使用场景

1
2
3
4
5
6
7
8
9
<!-- 预获取用户可能访问的下一页 -->
<link rel="prefetch" href="/products/page-2.html">

<!-- 预获取下一页的 JavaScript -->
<link rel="prefetch" href="/js/product-detail.js">

<!-- DNS 预解析(提升后续连接速度) -->
<link rel="dns-prefetch" href="https://api.example.com">
<link rel="preconnect" href="https://fonts.googleapis.com">

prefetch vs preload

特性 preload prefetch
优先级
时机 立即 浏览器空闲时
用途 当前页关键资源 下一个页面资源
带宽 占用当前带宽 不占用当前带宽

1.4 预连接(Preconnect)

preconnect 用于提前建立与第三方域名的连接,包括 DNS 解析、TCP 握手和 TLS 协商。

1
2
3
4
5
6
7
8
9
<!-- 预连接字体服务 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- 预连接 API 服务 -->
<link rel="preconnect" href="https://api.example.com">

<!-- DNS 预解析(更轻量,只做 DNS 查询) -->
<link rel="dns-prefetch" href="https://cdn.example.com">

使用场景

  • 第三方字体服务
  • CDN 资源
  • API 端点
  • 社交媒体插件

1.5 其他资源提示

1
2
3
4
5
<!-- 预渲染整个页面(适用于确定用户会访问的页面) -->
<link rel="prerender" href="https://example.com/next-page.html">

<!-- 提示资源优先级 -->
<link rel="modulepreload" href="app.js">

2. 图片与字体优化

2.1 图片优化

2.1.1 图片格式选择

格式 特点 适用场景
JPEG 有损压缩,文件小 照片、复杂图像
PNG 无损压缩,支持透明 图标、简单图形
WebP 现代格式,压缩比高 通用(需兼容性处理)
AVIF 最新格式,压缩比极高 现代浏览器
SVG 矢量图,无损缩放 图标、简单图形
GIF 支持动画 简单动画

2.1.2 响应式图片

**使用 srcsetsizes**:

1
2
3
4
5
6
7
8
9
<img 
srcset="image-320w.jpg 320w,
image-640w.jpg 640w,
image-1280w.jpg 1280w"
sizes="(max-width: 640px) 100vw,
(max-width: 1280px) 50vw,
1280px"
src="image-640w.jpg"
alt="响应式图片">

使用 <picture> 元素(格式选择)

1
2
3
4
5
6
7
<picture>
<!-- 现代格式优先 -->
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<!-- 降级方案 -->
<img src="image.jpg" alt="描述">
</picture>

结合响应式和格式选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<picture>
<source
media="(max-width: 640px)"
srcset="small.avif 1x, small-2x.avif 2x"
type="image/avif">
<source
media="(max-width: 640px)"
srcset="small.webp 1x, small-2x.webp 2x"
type="image/webp">
<source
media="(max-width: 640px)"
srcset="small.jpg 1x, small-2x.jpg 2x">
<source
srcset="large.avif 1x, large-2x.avif 2x"
type="image/avif">
<source
srcset="large.webp 1x, large-2x.webp 2x"
type="image/webp">
<img
src="large.jpg"
srcset="large.jpg 1x, large-2x.jpg 2x"
alt="响应式图片">
</picture>

2.1.3 懒加载(Lazy Loading)

原生懒加载(现代浏览器)

1
<img src="image.jpg" alt="描述" loading="lazy">

传统方法(使用 Intersection Observer)

1
2
3
4
5
<img 
data-src="image.jpg"
src="placeholder.jpg"
alt="描述"
class="lazy-load">
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
document.addEventListener('DOMContentLoaded', () => {
const images = document.querySelectorAll('.lazy-load');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy-load');
observer.unobserve(img);
}
});
});

images.forEach(img => imageObserver.observe(img));
});

2.1.4 图片优化最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!-- 1. 使用现代格式 -->
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero 图片">
</picture>

<!-- 2. 设置合适的尺寸 -->
<img
srcset="thumbnail.jpg 150w, large.jpg 800w"
sizes="(max-width: 600px) 150px, 800px"
src="thumbnail.jpg"
alt="描述"
loading="lazy">

<!-- 3. 提供占位符 -->
<img
src="image.jpg"
alt="描述"
loading="lazy"
width="800"
height="600">

<!-- 4. 使用 CSS 实现图片效果而非大图 -->
<style>
.blur-load {
filter: blur(10px);
transition: filter 0.3s;
}
.blur-load.loaded {
filter: blur(0);
}
</style>

2.2 字体优化

2.2.1 字体加载策略

**关键字体使用 preload**:

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

<!-- 字体定义 -->
<style>
@font-face {
font-family: 'Main';
src: url('fonts/main-bold.woff2') format('woff2');
font-weight: 700;
font-display: swap;
}
</style>
</head>

字体显示策略(font-display

行为 适用场景
auto 浏览器默认 不推荐
block 阻塞 3s,显示回退字体 品牌字体
swap 立即显示回退字体 推荐
fallback 阻塞 100ms,3s 超时后放弃 非关键字体
optional 阻塞 100ms,不下载则放弃 装饰性字体

推荐配置

1
2
3
4
5
6
7
8
9
10
11
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap; /* 推荐使用 swap */
}

body {
font-family: 'CustomFont', 'Arial', sans-serif; /* 提供回退字体 */
}

2.2.2 字体子集化(Subsetting)

减少字体文件大小,只包含需要的字符。

1
2
<!-- 使用 Google Fonts 的 &text 参数 -->
<link href="https://fonts.googleapis.com/css2?family=Roboto&text=Hello" rel="stylesheet">

2.2.3 字体优化最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<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/roboto-bold.woff2"
as="font"
type="font/woff2"
crossorigin>

<!-- 3. 加载字体 -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">

<!-- 或者使用本地字体 -->
<style>
@font-face {
font-family: 'Roboto';
src: url('fonts/roboto-regular.woff2') format('woff2');
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: 'Roboto';
src: url('fonts/roboto-bold.woff2') format('woff2');
font-weight: 700;
font-display: swap;
}
</style>
</head>

3. 关键渲染路径(Critical Rendering Path)与阻塞点

3.1 关键渲染路径概述

关键渲染路径(CRP)是从接收 HTML、CSS 和 JavaScript 到渲染出可视化内容的过程。

渲染步骤

  1. DOM 构建:解析 HTML 创建 DOM 树
  2. CSSOM 构建:解析 CSS 创建 CSSOM 树
  3. 渲染树构建:合并 DOM 和 CSSOM
  4. 布局(Layout):计算元素位置和大小
  5. 绘制(Paint):填充像素
  6. 合成(Composite):图层合成

3.2 渲染阻塞资源

3.2.1 CSS 阻塞渲染

默认行为

  • <link> 标签会阻塞渲染
  • CSS 必须完全下载并解析后才能渲染

优化方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 内联关键 CSS -->
<style>
/* 首屏关键样式 */
body { margin: 0; }
.header { ... }
.hero { ... }
</style>

<!-- 非关键 CSS 使用 media 属性避免阻塞 -->
<link rel="stylesheet" href="print.css" media="print">
<link rel="stylesheet" href="large.css" media="(min-width: 800px)">

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

3.2.2 JavaScript 阻塞渲染

默认行为

  • <script> 会阻塞 HTML 解析
  • 可能阻塞 CSSOM 构建

优化方法

1
2
3
4
5
6
7
8
9
10
11
<!-- 使用 defer 延迟执行 -->
<script defer src="main.js"></script>

<!-- 使用 async 异步执行(适用于独立脚本) -->
<script async src="analytics.js"></script>

<!-- 内联关键脚本放在 </body> 前 -->
<script>
// 关键初始化代码
</script>
</body>

3.3 优化关键渲染路径

3.3.1 优化 HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<!-- 关键 CSS 内联 -->
<style>
/* Critical CSS for above-the-fold content */
body { margin: 0; font-family: Arial; }
.header { ... }
.hero { ... }
</style>

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

<!-- 非关键 CSS 异步加载 -->
<link rel="preload" href="styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>
<body>
<!-- 首屏内容 -->
<header class="header">...</header>
<main class="hero">...</main>

<!-- 延迟加载的内容 -->
<section class="content">...</section>

<!-- JavaScript 放在底部 -->
<script defer src="main.js"></script>
</body>
</html>

3.3.2 关键资源优先级

识别关键资源

  • 首屏可见内容
  • 关键字体
  • 首屏图片
  • 关键 JavaScript(如交互逻辑)

优化策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<head>
<!-- 1. 内联关键 CSS -->
<style>
/* Critical CSS */
</style>

<!-- 2. 预加载关键字体 -->
<link rel="preload" href="font.woff2" as="font" crossorigin>

<!-- 3. 预加载首屏图片 -->
<link rel="preload" href="hero.jpg" as="image">

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

3.4 性能指标

3.4.1 关键性能指标(Web Vitals)

指标 说明 目标值
FCP (First Contentful Paint) 首次内容绘制 < 1.8s
LCP (Largest Contentful Paint) 最大内容绘制 < 2.5s
FID (First Input Delay) 首次输入延迟 < 100ms
CLS (Cumulative Layout Shift) 累积布局偏移 < 0.1
TTI (Time to Interactive) 可交互时间 < 3.8s

3.4.2 优化检查清单

HTML 优化

  • ✅ 最小化 HTML 大小
  • ✅ 减少 DOM 深度
  • ✅ 延迟加载非关键内容
  • ✅ 使用语义化 HTML

CSS 优化

  • ✅ 内联关键 CSS
  • ✅ 避免 @import
  • ✅ 移除未使用的 CSS
  • ✅ 使用媒体查询延迟加载

JavaScript 优化

  • ✅ 使用 deferasync
  • ✅ 代码分割和懒加载
  • ✅ 最小化 JavaScript 大小
  • ✅ 避免长时间运行的脚本

资源优化

  • ✅ 压缩图片
  • ✅ 使用现代图片格式
  • ✅ 优化字体加载
  • ✅ 启用 Gzip/Brotli 压缩

3.5 实践示例

优化的 HTML 页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>优化的页面</title>

<!-- 关键 CSS 内联 -->
<style>
/* Reset and critical styles */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.6;
}
.header { background: #333; color: #fff; padding: 1rem; }
.hero { padding: 2rem; text-align: center; }
</style>

<!-- 预连接外部资源 -->
<link rel="preconnect" href="https://fonts.googleapis.com">

<!-- 预加载关键字体 -->
<link rel="preload"
href="fonts/main.woff2"
as="font"
type="font/woff2"
crossorigin>

<!-- 预加载首屏图片 -->
<link rel="preload"
href="images/hero.jpg"
as="image">

<!-- 异步加载非关键 CSS -->
<link rel="preload"
href="styles/non-critical.css"
as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles/non-critical.css"></noscript>
</head>
<body>
<header class="header">
<h1>网站标题</h1>
</header>

<main class="hero">
<picture>
<source srcset="images/hero.avif" type="image/avif">
<source srcset="images/hero.webp" type="image/webp">
<img src="images/hero.jpg" alt="Hero" loading="eager">
</picture>
<h2>欢迎访问</h2>
</main>

<section class="content">
<!-- 延迟加载的内容 -->
</section>

<!-- JavaScript 延迟加载 -->
<script defer src="js/main.js"></script>

<!-- 异步加载分析脚本 -->
<script async src="https://www.google-analytics.com/analytics.js"></script>
</body>
</html>

4. 总结

HTML 层面的性能优化要点:

  1. 资源加载

    • 使用 defer/async 优化脚本加载
    • 使用 preload 预加载关键资源
    • 使用 prefetch 预取下一页面资源
    • 使用 preconnect 提前建立连接
  2. 图片优化

    • 选择合适格式(WebP、AVIF)
    • 使用响应式图片
    • 实施懒加载
    • 提供占位符
  3. 字体优化

    • 预加载关键字体
    • 使用 font-display: swap
    • 提供回退字体
    • 考虑字体子集化
  4. 关键渲染路径

    • 内联关键 CSS
    • 延迟非关键资源
    • 优化资源加载顺序
    • 减少渲染阻塞

通过合理运用这些技术,可以显著提升页面的加载速度和用户体验。