Skip to content

图片优化指南

图片通常是网页中最大的资源,优化图片可以显著提升网站加载速度和用户体验。本文介绍 VitePress 中的图片优化最佳实践。

为什么需要图片优化?

指标优化前优化后
页面加载时间5-10秒1-2秒
带宽消耗5-10MB500KB-1MB
Lighthouse 分数50-6090-100
用户体验长时间等待即时加载

图片格式选择

格式对比

格式透明度动画压缩率浏览器支持
JPEG⭐⭐⭐⭐⭐
PNG⭐⭐⭐⭐⭐
WebP极高⭐⭐⭐⭐
AVIF最高⭐⭐⭐
GIF⭐⭐⭐⭐⭐
SVG矢量⭐⭐⭐⭐⭐

选择建议

照片/截图 → WebP > JPEG > PNG
图标/Logo → SVG
简单图形 → SVG > PNG
动画图片 → WebP > GIF
需要透明 → WebP > PNG

图片压缩

手动压缩工具

工具类型特点
TinyPNG在线PNG/JPEG 压缩
Squoosh在线多格式、可调节
ImageOptimmacOS批量压缩
pngquantCLIPNG 有损压缩

构建时压缩

使用 Vite 插件自动压缩:

bash
npm install -D vite-plugin-imagemin
ts
// 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
![Logo](./images/logo.png){srcset="./images/logo-2x.png 2x, ./images/logo-3x.png 3x"}

sizes 属性

根据视口宽度选择图片:

markdown
![Banner](./images/banner.jpg){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
<!-- 默认行为 -->
![截图](./images/screenshot.png)

<!-- 显式指定 -->
![截图](./images/screenshot.png){loading="lazy"}

<!-- 禁用懒加载(首屏图片)-->
![Hero](./images/hero.png){loading="eager"}

懒加载最佳实践

markdown
<!-- 首屏图片:立即加载 -->
![Hero Banner](./images/hero.png){loading="eager" fetchpriority="high"}

<!-- 非首屏图片:懒加载 -->
![功能截图](./images/screenshot-1.png){loading="lazy"}
![功能截图](./images/screenshot-2.png){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-webp
ts
// 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
<!-- 推荐:指定尺寸 -->
![截图](./images/screenshot.png){width="800" height="600"}

<!-- 或使用 CSS -->
![截图](./images/screenshot.png){.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 svgo
js
// 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
<!-- 本地路径 -->
![图片](./images/photo.jpg)

<!-- CDN 路径 -->
![图片](https://cdn.example.com/images/photo.jpg)

动态 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

网络瀑布图分析

在浏览器开发者工具中:

  1. 打开 Network 面板
  2. 禁用缓存(Disable cache)
  3. 刷新页面
  4. 分析图片加载时间和顺序

相关资源

贡献者

加载中...

想要成为贡献者?

在 CNB 上参与贡献