返回首页

图片资源加载优化

布莱克2026-02-02 17:10已编辑
Tip:文章封面与内容无关,作者旅游时拍摄,因为没什么值得把四季都错过!

图片资源较大,如何优化?

起因:博客中上传的某些图片比较大,因为大部分是旅游时用相机拍摄的,所以在页面加载时,因为图片资源大,加载很缓慢,文字内容出来了,图片缓慢加载

图片资源较大可能会造成的问题:

影响维度具体表现与原因
网络请求每个图片都是一个独立的HTTP请求。浏览器对同一域名的并发请求数有限制(通常6-8个),大量大图片会阻塞后续关键资源(如JS、CSS)的加载。
下载时间假设用户网速为5Mbps,比如加载一张20MB图片就需要 32秒。多张这样的图片会使加载时间呈线性增长,用户可能在看到任何内容前就离开了。
渲染阻塞图片下载完成后,浏览器需要解码(将图片数据转为像素)和渲染,这需要消耗大量主线程CPU和内存,导致页面卡顿、滚动不流畅。
流量消耗对移动端用户极不友好,甚至可能直接耗尽其套餐。

拓宽:什么会影响页面的加载?

除了大图片,以下因素也会拖慢你的页面:

  • 关键请求链过长: HTML 加载了 JS,JS 执行后再去请求数据,数据返回后才去加载图片。这种串行依赖会增加白屏时间。
  • 资源排队 (Queuing): 浏览器对同一域名的并发连接有限,过多的静态资源(图标、小图)会导致请求排队。
  • JS 阻塞渲染: 放在 <head> 里的巨大 JS 文件会阻止浏览器解析 HTML,直到脚本下载并执行完毕。
  • 服务器响应延迟 (TTFB): 服务器处理逻辑太慢,或者没有开启 Gzip/Brotli 压缩。
  • 第三方脚本: 缓慢的统计插件、地图插件或广告脚本。

阿里云OSS优化

首先图片存储是使用阿里云OSS,使用 阿里云 OSS 图片处理(最快见效)

1. 样式别名(Style)

在阿里云 OSS 控制台预先定义好一套规则(比如缩放、质量、格式),给这套规则起个名字。

进入 OSS 控制台 -> 选中 Bucket -> 图片处理。可以设置多个样式别名

  • 格式: image.jpg?x-oss-process=style/stylename
  • 优点:
    • 代码整洁: 后缀非常短。
    • 动态调整:直接在 OSS 控制台修改 stylename 的定义,线上所有图片立即生效,无需改动任何代码。
    • 安全防盗: 可以配合“原图保护”使用。
  • 缺点: 需要先去控制台手动配置或通过 API 预设样式。

2. 自定义分隔符(最像静态资源)

在 OSS 控制台开启“自定义分隔符”,通常配合“样式别名”使用。

在同一页面找到“访问设置”,设置分隔符

  • 格式: image.jpg!stylename (用 ! 或 - 或 _ 代替冗长的 ?x-oss-process=style/)
  • 优点: URL 极其美观,看起来就像一个普通文件。
  • 缺点: 需要在控制台额外开启一项配置。

我是使用 样式别名 + 自定义分隔符(分隔符使用的!,看个人选择)

通过这样的设置,可能会有如下样式别名 !original,!preview等等(具体看你设置了哪些样式别名)

然后在前端项目中封装图片组件,在页面中适应img的地方改为封装后的组件

组件可传入类型,比如类型传入preview,则最终渲染的图片为 https://img.blackztt.cn/uploads/2026-01-26/656bae28f23559f4bd2cf490b078136a.jpg!preview (以该项目为例)

这样在图片加载时,会进行压缩,而不是原始图片尺寸,图片加载效率大幅提升


图片懒加载

懒加载会极大提升页面加载效率。

  • 带宽解放: 浏览器不再同时抢占带宽去下载很多张图片,而是优先下载页面的 CSS、JS 和首屏数据。
  • 首屏秒开: 用户不需要等待所有图片下载完就能看到网页内容。
  • 节省流量: 如果用户只看了开头就关闭了页面,后面的图片永远不会被下载,节省了你的 OSS 流量费

讲一下图片懒加载的实现:

首先需要讲到一个概念,叫做IntersectionObserver,是现代浏览器提供的一种高性能 API,专门用于异步监测一个元素是否进入了另一个元素(通常是视口)的可视区域

IntersectionObserver 的优势: 它是基于浏览器底层引擎实现的。你只需告诉浏览器“当这个元素进来时通知我”,浏览器会在内部优化的时间点发出通知,不会阻塞主线程

配置对象 { rootMargin: '50px' }

除了 rootMargin,其实共有三个核心参数:

  1. root: 指定参照的容器(默认为浏览器视口 null)。
  2. rootMargin: 相当于给视口加了“外边距”。你设置的 '50px' 意味着:图片还没进入视口,但在距离视口底部还有 50px 时,就会提前触发加载。目的: 消除用户滚动时的“白屏感”,让用户感觉图片是顺滑出现的


回调数据 entries

回调函数返回的是一个数组,因为一个观察者可以同时观察多个对象。

  • entry.isIntersecting: 这是一个布尔值。true 表示元素进入了视口,false 表示移出了视口。
  • entry.target: 当前触发状态变化的那个 DOM 元素。
  • entry.intersectionRatio: 目标元素当前出现在可视区域内的比例

对封装的图片组件继续封装,实现懒加载功能

因为是nuxt项目,贴出图片懒加载的部分实现

onMounted(() => {
  if ('IntersectionObserver' in window) {
    observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          //触发真实图片的加载
          isIntersecting.value = true
          // 加载完成后,停止观察该元素,释放内存
          if (observer) {
            observer.unobserve(entry.target)
          }
        }
      })
    }, {
      rootMargin: '50px' // 提前 50px 开始加载,提升用户体验
    })

    if (imgRef.value) {
      observer.observe(imgRef.value)
    }
  } else {
    // 降级处理:如果浏览器不支持 IntersectionObserver,直接加载
    isIntersecting.value = true
  }
})

onUnmounted(() => {
  if (observer) {
    observer.disconnect()
  }
})


assistant