移动前端性能优化最佳实践 —— 采用新一代格式 WebP 提供图片

2,529 阅读3分钟

发现问题

在使用 Lighthouse 审查 H5 页面的性能时,Lighthouse 给到了这样一条优化建议 ——

采用新一代格式提供图片

WebP 和 AVIF 等图片格式的压缩效果通常优于 PNG 或 JPEG,因而下载速度更快,消耗的数据流量更少。

那么,什么是 WebP,我们为什么要使用 WebP 呢?

WebP 是什么

WebP 是一种 现代图片格式,可以为 Web 上的图片提供卓越的无损和有损压缩,并且有损压缩的程度是可调的,因此开发者可以在 文件大小图片质量 之间进行 权衡

  • 与 PNG 相比,WebP 无损图片的大小要小 26%;
  • 在同等 SSIM(SSIM:结构相似性,是一种衡量两个图片相似度的指标)质量指数下,WebP 有损图片比 JPEG 图片小 25-34%;
  • 无损 WebP 支持透明度(也称为 alpha 通道),成本仅为 22% 额外字节。

WebP 格式旨在创建 体积更小质量更好 的图片,以帮助提升页面性能,改善用户体验。

如何获取 WebP 格式的图片

这里推荐一个字节开源的一站式图像智能处理平台 —— 火山引擎 veImageX 体验版

你可以在这个平台上,通过导入一个图片源文件,获取到任意格式的图片文件,且可以自由调节压缩质量,在线对比图片转换前后的效果。

WebP 支持度如何

WebP 的好处想必大家都领略到了,那么问题来了,各种浏览器对 WebP 的支持度如何呢?

我们可以通过 Can I use WebP ? 进行了解。

因为我个人关心的是移动端场景,并且 H5 页面只投放到 App 端内,所以我勾选了 tracked mobile,然后查看了 Safari on iOSAndroid Browser 这两种环境对 WebP 的支持情况。

结论:

  • iOS 从版本 14 才开始支持 WebP 格式的图片;
  • Android 从版本 4 开始支持 WebP 格式的图片,但是在版本 4.2 之前,不支持 WebP 的无损压缩 lossless、透明度 alpha 和动画 animation 功能。

所以,我并不能毫无顾虑地在项目中使用 WebP 格式的图片,需要做一些 兜底处理 —— 对于支持 WebP 格式的环境使用 WebP 格式,而不支持 WebP 格式的环境退而求其次,使用传统格式。

问题又双叒叕来了,我该如何判断当前浏览器环境是否支持 WebP 格式呢?

判断是否支持 WebP

对于这个问题,其实 Google 官方已经给出了推荐方案,我暂且将它命名为 嗅探大法 (如有雷同,纯属巧合......),原理很简单,我们尝试去加载一个 1×1 大小的 WebP 图片,如果加载成功了,并且可以获取到图片的 widthheight,说明当前浏览器环境支持 WebP,反之不支持。

我封装了一个工具函数 isSupportWebP,代码如下:

/**
 * @description 判断当前浏览器环境是否支持 WebP 格式
 * @param {string} [feature='lossless'] WebP 功能
 * @return {Promise<boolean>} 描述是否支持 WebP 格式的 Promise 对象
 */

const isSupportWebP = (feature = 'lossless') => {
  const testImages = {
    lossy: 'UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA',
    lossless: 'UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==',
    alpha: 'UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==',
    animation: 'UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA',
  };

  return new Promise(resolve => {
    const _image = new Image();
    _image.onload = () => {
      const result = _image.width > 0 && _image.height > 0;
      resolve(result);
    };
    _image.onerror = () => {
      resolve(false);
    };
    _image.src = `data:image/webp;base64,${testImages[feature]}`;
  });
};

你可以在 React Hooks 中这样使用 isSupportWebP

import React, { useState, useEffect } from 'react';
import { isSupportWebP } from './utils';

const App = () => {
  const [supportWebP, setSupportWebP] = useState(true);
  useEffect(() => {
    isSupportWebP().then(result => setSupportWebP(result));
  }, []);

  return (
    <div className="app">
      <img
        className="banner"
        src={require(`./images/banner.${supportWebP ? 'webp' : 'png'}`)}
        alt="banner image"
      />
    </div>
  );
};

export default App;

总结

虽是小白文章,却也诚意满满,感谢愿意花时间读到最后。

欢迎留下评论一起探讨技术!

参考文档