前端性能优化指南——从加载到渲染的全面提速

前端性能优化指南——从加载到渲染的全面提速

作者: CaoZH
日期: 2025-04-15
本文为原创教程


前端性能优化是一个老生常谈但又永不过时的话题。2025 年,用户对页面加载速度的期望值更高了——超过 3 秒加载的页面,53% 的用户会选择离开

本文按照「网络加载 → 资源优化 → 渲染优化 → 运行时优化」的路径,系统性地梳理前端性能优化的方法。

一、测量指标

核心 Web 指标(Core Web Vitals)

指标 全称 含义 理想值
LCP Largest Contentful Paint 最大内容绘制 < 2.5s
FID First Input Delay 首次输入延迟 < 100ms
CLS Cumulative Layout Shift 累计布局偏移 < 0.1

其他重要指标

1
2
3
4
- FCP(First Contentful Paint):首次内容绘制 < 1.8s
- TTFB(Time to First Byte):首字节时间 < 800ms
- TBT(Total Blocking Time):总阻塞时间 < 200ms
- SI(Speed Index):速度指数 < 3.4s

测量工具

1
2
3
4
5
6
7
8
# Lighthouse(最常用)
npx lighthouse https://your-site.com --view

# WebPageTest
# 访问 https://webpagetest.org

# Chrome DevTools
# Performance 面板 → Network 面板 → Coverage 面板

二、网络层面优化

1. 启用 HTTP/2

1
2
3
4
5
# Nginx 配置 HTTP/2
server {
listen 443 ssl http2; # http2 启用
# ...
}

HTTP/2 支持多路复用,一个连接并行请求,大幅减少连接开销。

2. 启用 Gzip / Brotli 压缩

1
2
3
4
5
6
7
8
9
10
11
# Nginx Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript image/svg+xml;
gzip_min_length 1000;
gzip_comp_level 6;
gzip_vary on;

# Brotli(压缩率比 Gzip 高 20%)
brotli on;
brotli_types text/plain text/css application/json application/javascript;
brotli_comp_level 6;

3. 使用 CDN

1
2
3
- 静态资源(JS/CSS/图片)走 CDN
- 推荐:Cloudflare(免费)、阿里云 CDN、AWS CloudFront
- 效果:减少 TTFB,全球加速

4. 资源预加载

1
2
3
4
5
6
7
8
9
10
11
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//api.example.com">

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

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

<!-- 预获取下一页 -->
<link rel="prefetch" href="/next-page.html">

三、资源优化

1. 图片优化

1
2
3
4
5
6
7
8
9
## 格式选择
- 照片:WebP / AVIF(比 JPEG 小 30-50%)
- 图标/Logo:SVG
- 截图:WebP

## 工具
- Sharp(Node.js):npm install sharp
- imagemin:npm install imagemin
- squoosh.app:在线压缩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Vite 配置图片压缩
import { defineConfig } from 'vite'
import imagemin from 'vite-plugin-imagemin'

export default defineConfig({
plugins: [
imagemin({
gifsicle: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.8, 0.9] },
webp: { quality: 80 }
})
]
})

2. 代码分割

1
2
3
4
5
6
7
8
// React — React.lazy
const UserList = React.lazy(() => import('./UserList'));

// Vue 3 — defineAsyncComponent
const UserList = defineAsyncComponent(() => import('./UserList.vue'));

// Webpack / Vite — 动态 import
import(/* webpackChunkName: "admin" */ './admin.js')

3. Tree Shaking

1
2
3
4
5
6
7
8
// package.json
{
"sideEffects": false // 启用 Tree Shaking
}

// 只导入需要的部分,不要全量引入
// ❌ import { Button } from 'antd'
// ✅ import Button from 'antd/es/button'

四、渲染优化

1. 关键渲染路径

1
2
3
4
5
6
7
8
9
10
<!-- 关键 CSS 内联,非关键 CSS 异步 -->
<!-- 推荐:将首屏 CSS 内联到 HTML head 中 -->
<style>
/* 首屏关键样式 */
.header { ... }
.hero { ... }
</style>

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

2. 虚拟滚动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 长列表优化(1000+ 条数据) -->
<script setup>
import { VirtualScroller } from 'vue-virtual-scroller'

// 万级列表只渲染可见区域的 DOM,大幅减少渲染开销
</script>

<template>
<VirtualScroller
:items="list"
:item-height="50"
:buffer="200"
>
<template #default="{ item }">
<div class="item">{{ item.name }}</div>
</template>
</VirtualScroller>
</template>

3. 避免布局抖动

1
2
3
4
5
6
7
8
9
10
11
/* CLS 优化:为动态内容预留空间 */
.image-container {
width: 100%;
aspect-ratio: 16 / 9; /* 预留宽高比 */
}

/* 使用 transform 替代 top/left 做动画 */
.animated {
transition: transform 0.3s ease; /* ✅ 不触发重排 */
/* transition: left 0.3s ease; */ /* ❌ 触发重排 */
}

五、运行时优化

1. 减少重渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script setup>
import { computed, shallowRef } from 'vue'

// 使用 computed 缓存计算结果
const totalPrice = computed(() => {
return items.value.reduce((sum, item) => sum + item.price, 0)
})

// 大数组使用 shallowRef
const userList = shallowRef([])

// v-memo 避免不必要的渲染
</script>

<template>
<div v-for="item in list" :key="item.id" v-memo="[item.name, item.price]">
{{ item.name }} - {{ item.price }}
</div>
</template>

2. Web Worker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// worker.js — 在独立线程中处理数据密集任务
self.onmessage = function(e) {
const result = heavyCalculation(e.data)
self.postMessage(result)
}

function heavyCalculation(data) {
// 大量数据处理,不影响主线程
return data.map(item => processItem(item))
}

// 主线程
const worker = new Worker('worker.js')
worker.postMessage(largeData)
worker.onmessage = (e) => {
console.log('处理完成:', e.data)
}

3. 防抖与节流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script setup>
import { debounce, throttle } from 'lodash-es'

// 搜索输入:用户停止输入后再请求
const search = debounce(async (keyword) => {
const results = await api.search(keyword)
searchResults.value = results
}, 300)

// 滚动事件:限制触发频率
const handleScroll = throttle(() => {
// 处理滚动
}, 100)
</script>

六、构建工具配置

Vite 优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
build: {
target: 'es2020',
minify: 'esbuild', // esbuild 比 terser 快 20 倍
cssMinify: 'lightningcss', // 更快的 CSS 压缩
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
ui: ['ant-design-vue'],
charts: ['echarts']
}
}
}
}
})

七、总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 优化的黄金法则

1. 先测量,后优化(用 Lighthouse 找到瓶颈)
2. 80% 的收益来自 20% 的工作(图片 + 代码分割 + 缓存)
3. 不要过度优化(代码可维护性 > 极致性能)

## 快速检查清单

□ 图片使用 WebP 格式
□ 启用 Gzip/Brotli 压缩
□ 关键 CSS 内联
□ 路由懒加载
□ 静态资源走 CDN
□ 合理使用缓存策略
□ 虚拟滚动处理长列表
□ 减少不必要的重渲染

首发于 CaoZH 的笔记