# 前端图片优化实战,首屏加载提速60%

做完一个项目,Lighthouse跑分一看:性能40分。打开Network面板,图片占了三兆多。

图片优化是前端性能优化里性价比最高的部分,改完立竿见影。今天就聊聊怎么优化。

一、选对格式

格式 特点 适用
JPEG 有损压缩,不支持透明 照片、商品图
PNG 无损,支持透明 图标、需要透明的图
WebP 比JPEG小25-35% 首选,现代浏览器都支持
AVIF 比WebP再小20-30% 新项目可以试

一个实测数据:同一张商品图,JPEG 180KB,WebP 95KB,AVIF 68KB。

用法:

html 复制代码
<picture>
    <source srcset="photo.avif" type="image/avif">
    <source srcset="photo.webp" type="image/webp">
    <img src="photo.jpg" alt="商品图片" loading="lazy">
</picture>

浏览器会自动选择支持的格式。老浏览器降级到JPEG,新浏览器用最小的那个。

二、压缩图片

macOS上可以用pngquant、jpegoptim,批量处理:

bash 复制代码
# PNG压缩
for f in *.png; do pngquant --quality=65-80 "$f"; done

# JPEG压缩
for f in *.jpg; do jpegoptim --max=80 "$f"; done

Node生态有sharp:

javascript 复制代码
const sharp = require('sharp');

async function compress(input, output) {
    await sharp(input)
        .resize({ width: 1200, withoutEnlargement: true })
        .webp({ quality: 80 })
        .toFile(output);
}

我做的一个项目,把30张商品图统一压到80质量、WebP格式,总体积从4.2MB降到1.1MB,首屏时间从3.8秒降到1.5秒。

三、响应式图片

不要给手机加载桌面端的大图。

html 复制代码
<img
    srcset="
        image-400.jpg   400w,
        image-800.jpg   800w,
        image-1200.jpg  1200w,
        image-1600.jpg  1600w
    "
    sizes="(max-width: 600px) 100vw,
           (max-width: 1200px) 50vw,
           33vw"
    src="image-800.jpg"
    alt="示例">

sizes告诉浏览器在不同视口下的显示宽度,浏览器挑最合适的图下载。

Vue项目里可以封个组件:

vue 复制代码
<template>
  <img
    :src="src"
    :srcset="srcset"
    :sizes="sizes"
    :alt="alt"
    :loading="loading"
  >
</template>

<script setup>
const props = defineProps({
  src: String,
  alt: { type: String, default: '' },
  widths: { type: Array, default: () => [400, 800, 1200] },
  sizes: { type: String, default: '100vw' },
  loading: { type: String, default: 'lazy' }
});

const srcset = computed(() =>
  props.widths.map(w => `${props.src}?w=${w} ${w}w`).join(', ')
);
</script>

配合图片CDN的动态处理,一张原图能生成任意尺寸。

四、懒加载

首屏之外的图片延后加载。

html 复制代码
<!-- 原生方案,最简单 -->
<img src="photo.jpg" loading="lazy" alt="图片">

兼容性问题可以用IntersectionObserver自己实现:

javascript 复制代码
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            observer.unobserve(img);
        }
    });
}, {
    rootMargin: '100px'  // 提前100px开始加载
});

document.querySelectorAll('img[data-src]').forEach(img => {
    observer.observe(img);
});

注意:首屏的图片不要懒加载 ,会影响LCP指标。首屏图片加loading="eager"和fetchpriority="high"。

五、占位与骨架

图片加载前留出空间,避免布局抖动(CLS)。

css 复制代码
.img-wrapper {
    aspect-ratio: 16 / 9;   /* 预设比例,图片加载完也不会跳动 */
    background: #f5f5f5;
    overflow: hidden;
}

.img-wrapper img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

aspect-ratio是解决CLS的利器,比padding-top的hack方案优雅得多。

六、关键图片预加载

html 复制代码
<link rel="preload" as="image" href="hero.webp">

首屏大图预加载,能明显改善LCP。

七、CDN + 缓存

nginx 复制代码
location ~* \.(jpg|jpeg|png|webp|avif)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

图片配合CDN,二次访问直接从边缘节点拿,几乎零延迟。

优化前后的对比

指标 优化前 优化后
图片总体积 4.2MB 1.1MB
首屏加载 3.8s 1.5s
LCP 4.1s 1.4s
CLS 0.24 0.02

落地顺序建议

  1. 先转WebP/AVIF,成本最低
  2. 再统一压缩质量到80
  3. 加懒加载
  4. 用aspect-ratio消除抖动
  5. 最后接CDN

按这个顺序做,每一步都有明显收益。

相关推荐
程序边界1 小时前
一台数据集成软件的拆解笔记(下):一致性、容错、跑批与迁移,KFS 真正难的地方在这
java·前端·笔记
不一样的少年_1 小时前
不花一分额外 API 钱,用现成的 Claude Code / Codex / Cursor 搭建 AI 个人知识库
前端·后端·ai编程
tryCbest2 小时前
ES6 ( ECMAScript 2015)核心语法
前端·ecmascript·es6
李小轰_Rex2 小时前
零开销 AI 数字人方案|Live2D 数字人驱动方案分享
前端·aigc
ModyQyW3 小时前
vite-plugin-uni-manifest 0.7:在 uni-app 读取之前生成
前端·uni-app
我命由我123453 小时前
Python 实用操作 - 文件操作(打开与关闭文件、读取文件、文件写入)
java·开发语言·前端·javascript·数据库·python·java-ee
修炼的dance3 小时前
批量转码前用 ffprobe 筛一遍,36 个文件能跳过 26 个
前端
费曼学习法3 小时前
从 0 到 1 上手 Super-Agent 架构:任务拆解、工具路由与失败回滚的三段实现
前端
2026小鱼AI3 小时前
当 LLM 生成的教学设计“不合格”:结构化校验、局部重生成与并发下的质量漂移
前端