原来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元素的介绍
相关推荐
星垂野1 分钟前
JavaScript 执行栈和执行上下文详解
前端·javascript
别叫我8 分钟前
Swift串行上传多个图片
前端
风铃喵游10 分钟前
核心骨架: 小程序双线程架构
前端·架构
天平19 分钟前
使用https-proxy-agent下载墙外资源
前端·javascript
每天吃饭的羊40 分钟前
面试题-函数类型的重载是啥意思
前端
迷途小码农么么哒42 分钟前
Element 分页表格跨页多选状态保持方案(十几行代码解决)
前端
前端付豪1 小时前
美团路径缓存淘汰策略全解析(性能 vs 精度 vs 成本的三难选择)
前端·后端·架构
abigale031 小时前
webpack+vite前端构建工具 -4webpack处理css & 5webpack处理资源文件
前端·css·webpack