图片优化指南
图片通常是网页中最大的资源,优化图片可以显著提升网站加载速度和用户体验。本文介绍 VitePress 中的图片优化最佳实践。
为什么需要图片优化?
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 页面加载时间 | 5-10秒 | 1-2秒 |
| 带宽消耗 | 5-10MB | 500KB-1MB |
| Lighthouse 分数 | 50-60 | 90-100 |
| 用户体验 | 长时间等待 | 即时加载 |
图片格式选择
格式对比
| 格式 | 透明度 | 动画 | 压缩率 | 浏览器支持 |
|---|---|---|---|---|
| JPEG | ❌ | ❌ | 高 | ⭐⭐⭐⭐⭐ |
| PNG | ✅ | ❌ | 中 | ⭐⭐⭐⭐⭐ |
| WebP | ✅ | ✅ | 极高 | ⭐⭐⭐⭐ |
| AVIF | ✅ | ✅ | 最高 | ⭐⭐⭐ |
| GIF | ✅ | ✅ | 低 | ⭐⭐⭐⭐⭐ |
| SVG | ✅ | ✅ | 矢量 | ⭐⭐⭐⭐⭐ |
选择建议
照片/截图 → WebP > JPEG > PNG
图标/Logo → SVG
简单图形 → SVG > PNG
动画图片 → WebP > GIF
需要透明 → WebP > PNG图片压缩
手动压缩工具
| 工具 | 类型 | 特点 |
|---|---|---|
| TinyPNG | 在线 | PNG/JPEG 压缩 |
| Squoosh | 在线 | 多格式、可调节 |
| ImageOptim | macOS | 批量压缩 |
| pngquant | CLI | PNG 有损压缩 |
构建时压缩
使用 Vite 插件自动压缩:
bash
npm install -D vite-plugin-imagemints
// docs/.vitepress/config.mts
import { defineConfig } from 'vitepress'
import viteImagemin from 'vite-plugin-imagemin'
export default defineConfig({
vite: {
plugins: [
viteImagemin({
gifsicle: { optimizationLevel: 3 },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
svgo: {
plugins: [
{ name: 'removeViewBox', active: false }
]
},
webp: { quality: 80 }
})
]
}
})响应式图片
srcset 属性
根据设备像素密度加载不同图片:
markdown
{srcset="./images/logo-2x.png 2x, ./images/logo-3x.png 3x"}sizes 属性
根据视口宽度选择图片:
markdown
{sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 600px"}picture 元素
使用自定义组件实现高级响应式:
vue
<!-- docs/.vitepress/components/ResponsiveImage.vue -->
<template>
<picture>
<!-- AVIF 格式(最新浏览器) -->
<source
:srcset="getSrcset('avif')"
type="image/avif"
>
<!-- WebP 格式(现代浏览器) -->
<source
:srcset="getSrcset('webp')"
type="image/webp"
>
<!-- 回退格式 -->
<img
:src="src"
:alt="alt"
:width="width"
:height="height"
loading="lazy"
decoding="async"
>
</picture>
</template>
<script setup lang="ts">
defineProps<{
src: string
alt: string
width?: number
height?: number
}>()
function getSrcset(format: string) {
const base = props.src.replace(/\.[^.]+$/, '')
return `${base}.${format}`
}
</script>使用:
markdown
<ResponsiveImage
src="./images/hero.jpg"
alt="Hero Image"
:width="1200"
:height="630"
/>懒加载
原生懒加载
VitePress 默认支持图片懒加载:
markdown
<!-- 默认行为 -->

<!-- 显式指定 -->
{loading="lazy"}
<!-- 禁用懒加载(首屏图片)-->
{loading="eager"}懒加载最佳实践
markdown
<!-- 首屏图片:立即加载 -->
{loading="eager" fetchpriority="high"}
<!-- 非首屏图片:懒加载 -->
{loading="lazy"}
{loading="lazy"}懒加载占位符
使用低质量图片占位符(LQIP):
vue
<!-- docs/.vitepress/components/LazyImage.vue -->
<template>
<img
:src="placeholder"
:data-src="src"
:alt="alt"
loading="lazy"
class="lazy-image"
@load="onLoad"
>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps<{
src: string
placeholder?: string
alt: string
}>()
const isLoaded = ref(false)
function onLoad() {
isLoaded.value = true
}
</script>
<style scoped>
.lazy-image {
filter: blur(10px);
transition: filter 0.3s ease;
}
.lazy-image.loaded {
filter: none;
}
</style>WebP 转换
批量转换为 WebP
使用命令行工具:
bash
# 安装 cwebp(Google WebP 工具)
# macOS
brew install webp
# 批量转换
for f in docs/public/images/*.png; do
cwebp -q 80 "$f" -o "${f%.png}.webp"
done使用 npm 脚本
json
// package.json
{
"scripts": {
"images:webp": "node scripts/convert-webp.js"
}
}js
// scripts/convert-webp.js
const sharp = require('sharp')
const fs = require('fs')
const path = require('path')
const imagesDir = 'docs/public/images'
async function convertToWebp(filePath) {
const outputPath = filePath.replace(/\.[^.]+$/, '.webp')
await sharp(filePath)
.webp({ quality: 80 })
.toFile(outputPath)
console.log(`Converted: ${filePath} -> ${outputPath}`)
}
async function main() {
const files = fs.readdirSync(imagesDir)
for (const file of files) {
if (/\.(png|jpe?g)$/i.test(file)) {
await convertToWebp(path.join(imagesDir, file))
}
}
}
main()Vite 插件自动转换
bash
npm install -D vite-plugin-webpts
// docs/.vitepress/config.mts
import { defineConfig } from 'vitepress'
import { VitePluginWebp } from 'vite-plugin-webp'
export default defineConfig({
vite: {
plugins: [
VitePluginWebp({
// 大于 10KB 的图片才转换
limit: 10240,
quality: 80
})
]
}
})图片尺寸优化
指定图片尺寸
始终指定图片宽高,避免布局偏移(CLS):
markdown
<!-- 推荐:指定尺寸 -->
{width="800" height="600"}
<!-- 或使用 CSS -->
{.screenshot-img}css
.screenshot-img {
width: 100%;
max-width: 800px;
height: auto;
aspect-ratio: 4 / 3;
}宽高比容器
处理动态尺寸图片:
vue
<!-- docs/.vitepress/components/AspectRatioImage.vue -->
<template>
<div class="aspect-container" :style="{ aspectRatio }">
<img :src="src" :alt="alt" loading="lazy" />
</div>
</template>
<script setup lang="ts">
defineProps<{
src: string
alt: string
aspectRatio?: string // 如 '16/9', '4/3'
}>()
</script>
<style scoped>
.aspect-container {
position: relative;
width: 100%;
overflow: hidden;
background: #f5f5f5;
}
.aspect-container img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
</style>SVG 优化
SVGO 压缩
bash
npm install -D svgojs
// svgo.config.js
module.exports = {
multipass: true,
plugins: [
'preset-default',
'removeDimensions',
'removeViewBox',
'cleanupIDs'
]
}bash
# 批量优化
npx svgo -f docs/public/icons -o docs/public/icons-optimized内联 SVG
小型 SVG 可直接内联:
vue
<template>
<svg width="24" height="24" viewBox="0 0 24 24">
<path d="M12 2L2 7l10 5 10-5-10-5z" fill="currentColor"/>
</svg>
</template>SVG 作为组件
vue
<!-- docs/.vitepress/components/IconLogo.vue -->
<template>
<svg viewBox="0 0 24 24" :class="sizeClass">
<path d="..." fill="currentColor" />
</svg>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
size?: 'sm' | 'md' | 'lg'
}>()
const sizeClass = computed(() => ({
'w-4 h-4': props.size === 'sm',
'w-6 h-6': props.size === 'md',
'w-8 h-8': props.size === 'lg'
}))
</script>图片 CDN
使用 CDN 加速
markdown
<!-- 本地路径 -->

<!-- CDN 路径 -->
动态 CDN 转换
vue
<!-- docs/.vitepress/components/CdnImage.vue -->
<template>
<img
:src="cdnUrl"
:alt="alt"
:width="width"
:height="height"
loading="lazy"
/>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
src: string
alt: string
width?: number
height?: number
quality?: number
format?: 'webp' | 'avif' | 'jpg'
}>()
const cdnUrl = computed(() => {
const url = new URL(props.src, 'https://cdn.example.com')
// 添加转换参数
if (props.width) url.searchParams.set('w', String(props.width))
if (props.height) url.searchParams.set('h', String(props.height))
if (props.quality) url.searchParams.set('q', String(props.quality))
if (props.format) url.searchParams.set('f', props.format)
return url.toString()
})
</script>使用:
markdown
<CdnImage
src="/images/photo.jpg"
alt="Photo"
:width="800"
:height="600"
:quality="80"
format="webp"
/>预加载策略
预加载关键图片
ts
// docs/.vitepress/config.mts
export default defineConfig({
head: [
// 预加载首屏关键图片
['link', {
rel: 'preload',
href: '/images/hero.webp',
as: 'image',
type: 'image/webp'
}],
// 预连接 CDN
['link', {
rel: 'preconnect',
href: 'https://cdn.example.com'
}]
]
})图片预取
vue
<script setup>
import { onMounted } from 'vue'
// 预取下一页图片
onMounted(() => {
const images = [
'/images/next-page-1.webp',
'/images/next-page-2.webp'
]
images.forEach(src => {
const link = document.createElement('link')
link.rel = 'prefetch'
link.href = src
link.as = 'image'
document.head.appendChild(link)
})
})
</script>图片优化检查清单
| 检查项 | 状态 |
|---|---|
| 选择正确的图片格式 | ☐ |
| 图片已压缩 | ☐ |
| 使用 WebP/AVIF 格式 | ☐ |
| 设置图片宽高属性 | ☐ |
| 非首屏图片懒加载 | ☐ |
| 首屏图片预加载 | ☐ |
| 响应式图片配置 | ☐ |
| 使用 CDN 加速 | ☐ |
| SVG 已优化 | ☐ |
| 添加 alt 属性 | ☐ |
性能测量
Lighthouse 检测
bash
# 安装 Lighthouse CLI
npm install -g lighthouse
# 运行检测
lighthouse https://your-domain.com --view网络瀑布图分析
在浏览器开发者工具中:
- 打开 Network 面板
- 禁用缓存(Disable cache)
- 刷新页面
- 分析图片加载时间和顺序