原来vue-lazyload也会对页面性能有影响

前言

前端谈到站点图片优化的策略,一定绕不过懒加载。比如开发Vue应用首选的方案大概会是VueLazyLoad, 但大部分人在做懒加载技术方案选型的时候可能会忽略它们会带来的一些副作用。

VueLazyload 的基本原理

  1. 用户提供srcloadingerror三张图片,分别表示要加载图片,图片加载中显示的占位图,图片加载错误时显示的占位图
  2. VueLazyload 在初始化时会显示loading占位图,然后在判断图片元素进入视口后,通过 jsnew Image()创建一个图片实例异步加载src指定的图片
  3. 图片加载成功显示src指定的图片,图片加载失败后显示error指定的图片

浏览器资源下载优先级

VueLazyload的问题前,先简单介绍一下资源获取优先级(priority)的概念。

资源获取优先级(priority),是浏览器在加载网页时,决定优先下载哪些资源、延后加载哪些资源的机制。

浏览器在下载资源图片、脚本、css等资源时,会为这些资源分配一个提取priority,以便以最佳顺序下载它们。 比如图片资源下载时一般会被分配为Low优先级,但是位于视口内的图片优先级可能会被提升为High,被提升为High后浏览器会更早的去下载这些图片资源,从而更快发生 LCP。

VueLazyload带来的性能问题

结合上面的分析,不难发现VueLazyload可能出现的问题,因为所有(即使在视口中)的图片,都是通过new Image()的方式获取,这样就会导致图片的优先级只能是Low,浏览器可能会优先下载优先级较高的资源,推迟下载图片资源,因为图片下载时间点被推迟,可能让用户觉得页面加载慢,如果是轮播图这种视口区域占比比较大的元素也会导致核心性能指标LCP的时间变大。

一个简单的例子模拟一下VueLazyload加载图片

html 复制代码
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Test</title>
  </head>
  <body>
    <div>
      <img id="myImg" src="./test/loading.png" />
    </div>
  </body>

  <script>
    const img = new Image();
    img.onload = () => {
      const imdDom = document.querySelector('#myImg');
      imdDom.src = img.src;
    };
    img.src = './test/test.jpg';
    // 插入5个css和脚本
    for (let i = 1; i <= 5; i++) {
      const link = document.createElement('link');
      const script = document.createElement('script');
      link.rel = 'stylesheet';
      link.href = `./test/${i}.css`;
      document.head.appendChild(link);
      script.src = `./test/${i}.js`;
      document.head.appendChild(script);
    }
  </script>
</html>

通过谷歌的Network面板观察

PS: 为了能够看到测试效果,把网络调整成了Slow 4G

发现图片的下载优先级为Low,所有css资源的下载优先级为Highest

通过图片资源的Timing面板也看到,图片请求在请求队列中等待了1.11s,这就导致了用户可能会更晚的看到图片资源,带来不好的用户体验。

LCP性能指标也受影响。

尝试现代的懒加载方案

针对首屏视口的图片不要用懒加载,而是直接把图片地址写在src属性里面,如果不能确定图片首屏的位置,建议在图片标签里面加上loading="lazy"实现延迟加载,目前该方案兼容性也很不错。MDN文档传送门

接下来用这种方案改写上面的例子

html 复制代码
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Test</title>

    <style>
      #myImg {
        width: 100%;
        height: auto;
        background-image: url('./test/loading.png');
      }
    </style>
  </head>
  <body>
    <div>
      <img id="myImg" src="./test/test.jpg" loading="lazy" />
    </div>
  </body>

  <script>
    for (let i = 1; i <= 5; i++) {
      const link = document.createElement('link');
      const script = document.createElement('script');
      link.rel = 'stylesheet';
      link.href = `./test/${i}.css`;
      document.head.appendChild(link);
      script.src = `./test/${i}.js`;
      document.head.appendChild(script);
    }
  </script>
</html>

loading图片设置成图片元素的背景,看一下效果。

发现图片的下载优先级由Low提升成了High

图片请求在请求队列中等待了840ms ,比之前大概提升了0.3s

LCP性能指标也得到了提升。

接下来试试直接把loading="lazy"去掉再试试。

请求的排毒时间变为了14.3ms,基本上没有了排队时间。

LCP性能指标更是得到大幅度提升。

总结

  1. 首屏加载的大图避免使用任何懒加载方案,从而更快发生LCP
  2. 对于不能确定是否会在首屏显示的大图,可以考虑使用loading="lazy"的懒加载方案

参考文档

  1. 使用 Fetch Priority API 优化资源加载
  2. MDN对img元素的介绍
相关推荐
Ticnix22 分钟前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人25 分钟前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl29 分钟前
OpenClaw 深度技术解析
前端
崔庆才丨静觅33 分钟前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人41 分钟前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼44 分钟前
shadcn/ui,给你一个真正可控的UI组件库
前端
布列瑟农的星空1 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust
Mr Xu_1 小时前
Vue 3 中计算属性的最佳实践:提升可读性、可维护性与性能
前端·javascript
jerrywus1 小时前
我写了个 Claude Code Skill,再也不用手动切图传 COS 了
前端·agent·claude
玖月晴空1 小时前
探索关于Spec 和Skills 的一些实战运用-Kiro篇
前端·aigc·代码规范