原来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元素的介绍
相关推荐
小蜜蜂嗡嗡2 小时前
【flutter对屏幕底部有手势区域(如:一条横杠)导致出现重叠遮挡】
前端·javascript·flutter
伍哥的传说3 小时前
Vue 3 useModel vs defineModel:选择正确的双向绑定方案
前端·javascript·vue.js·definemodel对比·usemodel教程·vue3.4新特性·vue双向绑定
鼠鼠我捏,要死了捏8 小时前
深入解析Java NIO多路复用原理与性能优化实践指南
java·性能优化·nio
胡gh8 小时前
页面卡成PPT?重排重绘惹的祸!依旧性能优化
前端·javascript·面试
言兴8 小时前
# 深度解析 ECharts:从零到一构建企业级数据可视化看板
前端·javascript·面试
山有木兮木有枝_8 小时前
TailWind CSS
前端·css·postcss
烛阴9 小时前
TypeScript 的“读心术”:让类型在代码中“流动”起来
前端·javascript·typescript
杨荧9 小时前
基于Python的农作物病虫害防治网站 Python+Django+Vue.js
大数据·前端·vue.js·爬虫·python
Moment10 小时前
毕业一年了,分享一下我的四个开源项目!😊😊😊
前端·后端·开源
国科安芯10 小时前
高速CANFD收发器ASM1042在割草机器人轮毂电机通信系统中的适配性研究
网络·单片机·嵌入式硬件·性能优化·机器人·硬件工程