方案价值

  • CDN 厂商无关

  • 节省 CDN 处理费用(相对其他方案)

应用场景

一般情况下,postcss 处理插件默认引入。

覆盖用户图片上传入口,将用户上传的图片转换为 webp + 原格式,在兼容的设备上均以 webp 呈现。如果原图被 tinypng 压缩过,且压缩率良好,可以不使用 webp 进行处理。

如何使用:

图片标签

使用组件库提供的图片组件

  注意在升级组件到2.3.x

  此时对于modal有些许调整,请仔细查看升级文档 https://gitlab.yc345.tv/frontend/onion-ui/-/releases 。

  • 自动引入 import '@guanghe-pub/onion-ui/lib/base-css.css'

  • ui包升级

    "@guanghe-pub/onion-ui": "2.3.4"
  • babel包升级

    "@guanghe-pub/babel-plugin-import-onion-ui": "1.1.4"
import { OIImgLoad } from "@guanghe-pub/onion-ui";

<OIImgLoad
   webp="custom"
   src="https://fp.yangcong345.com/vip/img/1.d7061977.png"
   alt="洋葱学园"/>

// 在支持webp的浏览器中,加载的图片地址会变为
// https://fp.yangcong345.com/vip/img/1.d7061977.webp

背景图片css

  1. step1 : 项目配置postcss.config插件

    plugins: {
      '@guanghe-pub/postcss-plugin-auto-webp': {
        exclude: ['node_modules'],
      },
    },
  1. step2: html中写入一段代码

<script>
      const isSupport =
        !![].map &&
        document
          .createElement('canvas')
          .toDataURL('image/webp')
          .indexOf('data:image/webp') === 0
      console.log('🚗🚗🚗🚗🚗 检测当前是否支持webp', isSupport)
      if (!isSupport) {
        document.body.setAttribute('no-webp', 'true')
      }
    </script>
  1. step3: 上传图片的时候选择支持webp类型

然后代码中正常使用普通格式

.att1 {
  background: url('xxxxxx.b37954ea8eccc976d71dedb0__w.jpeg')
    no-repeat 100% / 100%;
}

上传入口

使用新版支持webp的上传组件 升级uplaod包,默认支持webp

https://npm.yc345.tv/-/web/detail/@guanghe-pub/yc-upload

方案实现:

前置实现 - Upload Hook

  • 前端:

    • 在上传图片接口表单里,增加接收两个参数(可选):

message Request{
     bool webp = 1;   // 是否开启转换为 webp,默认为 true
     bool convertName = 2 ;   // 是否转换文件名,默认为 true
 }
  • 后端:

    • 根据请求表单参数,后端处理在上传图片时,分别进行不同处理。

      • webp 为 true 或为空时:

        • 同时上传源文件 + 根据源文件类型,按约定命名的转换+压缩后的 webp(可选)。:

        • 接口返回增加 convert 字段,返回转换后的 webp 图片的地址。

      • webp 为 false 时:

        • 按以前操作处理,并且屏蔽 convertName 参数的值。

      • convertName 为 true 或为空时:

        • 将源文件名转换为 xxx__w.{png/jpeg},意为将源文件标记为支持 webp,方便图片组件进行处理。

      • convertName 为 false 时:

        • 不做任何转换文件名处理

    • 边界情况:由于 webp 可能存在转换失败的情况,建议考虑先处理 webp 的转换,而不是先上传源文件,如果 webp 转换失败,则上传源文件名,不加 __w 后缀。

返回结果例: webp true convertName true

{
  "code": 200,
  "data": {
    "key": "https://fp.yangcong345.com/middle/1.0.0/shareCard31__w.png",
    "convert": "https://fp.yangcong345.com/middle/1.0.0/shareCard31__p.webp"
  }
}

例:

foobar.png => foobar__w.png + foobar__p.webp foobar.jpg => foobar__w.jpg + foobar__j.webp foobar.jpeg => foobar__w.jpeg + foobar__je.webp

源文件:

"__w" 指该图片支持 webp,前端使用到该图的地方,可以安全进行 webp 升级/回退。

转换后:

“p” 指支持 alpha 通道的 PNG 格式,即支持透明通道的 PNG 格式。 “j” 指 JPG 格式。 “je” 指 JPEG 格式。

TODO & Check List:
  1. 指定转换参数

    quality=80         # ❓ 图片质量是否指定为 80
    mode=RGB           # ❓ 色彩空间是否指定为 RGB,需要跟设计同学的图片标准沟通
    lossless=false     # ❓ 是否开启无损压缩,与图片质量互斥
  2. libwebp 图像质量与压缩选项

    1. -q <float> 默认值:75 中文名:图像质量(0 至 100)

    2. -m <int> 默认值:4 中文名:压缩方法(0 至 6,数值越大压缩越好但速度越慢)

    3. -lossless 默认值:无 中文名:无损压缩

    4. -preset <default|photo|picture|drawing|icon|text> 默认值:default 中文名:预设设置

  3. 转换结果是否包含元数据

<img/> 标签图片的处理

基于 Onion-ui ImgLoad 实现通用 <AutoWebp /> 组件。

实现方案和实现原理:

  1. 利用 Picture 的回退特性,在浏览器层级自动判断加载何种类型的图片。

    1.     例:

    <AutoWebp alt="foobar" src="https://fp.yangcong345.com/middle/1.0.0/auto-webp-logo__p.webp" click={foobar} />

        转换结果等同于:

    <picture>
      <source srcset="https://fp.yangcong345.com/middle/1.0.0/auto-webp-logo__p.webp" type="image/webp">
      <img alt="Vue logo" width="128" height="128" src="https://fp.yangcong345.com/middle/1.0.0/auto-webp-logo.png" onClick="foobar">
    </picture>
  2. 透传所有原生事件到 picture > img 标签上,由于 picture 不会影响布局,所以该 picture > img 表现形式等同于原生 img 标签。

  3. 考察下一步废弃 <= Android 5.x 后,是否可以使用原生 Web Component 实现该组件(跨框架、体积小、0️⃣运行时)。

CSS url image 图片的处理

使用 postcss 插件: __guanghe-pub/postcss-plugin-auto-webp 是一个转化 background-image: url(xxx.(png|jpeg...)) 属性到 webp 的插件,支持在不支持的浏览器下,自动回退到原始格式。

实现方案和实现原理:

  1. 在 Dom 文档加载完成后(document.ready),对该浏览器是否支持 webp 进行判断,如果支持 webp,则给 body 一个 attr:webp,即:

    <body no-webp/>
  2. postcss 将 scss/less 中所有含有 url(xxx.{png/jpg...}) 的 class 生成一个 [no-webp] [.selector] 的副本,仅在 #app webp 属性生效时加载 webp 图片,其他时候还是加载原图。 例:

    .test{
      background: url(bg__p.webp);
      background-image: url(bg-img__j.webp);
      mask-image: url(mask__je.webp);
      border-image: url(border-image.png);
      // ...
      
      .foobar{
        border-image: url(foobar-border-image__j.webp);
        // ... 
      }
    }

        转换结果:

    .test {                                          // <-- 不做任何处理
      background: url(bg__p.webp);
      background-image: url(bg-img__j.webp);
      mask-image: url(mask__je.webp);
      border-image: url(border-image.png);
    }
    
    .test .foobar{                                   // <-- 不做任何处理
      border-image: url(foobar-border-image__j.webp);
    }
    
    [no-webp] .test{                                 // <-- ♻️ 添加不支持 webp 时的回退
      background: url(bg.png);
      background-image: url(bg-img.jpg);
      mask-image: url(mask.jpeg);
    }
    
    [no-webp] .test .foobar{                         // <-- ♻️ 添加不支持 webp 时的回退
      border-image: url(foobar-border-image__p.webp); // <-- 🌲 包含子层级的转换结果
    }

方案优势

  1. 拓展性:未来放弃不兼容 webp 的设备时,我们只需要将 @guanghe-pub/auto-webp-postcss-plugin 插件做一个废弃。

  2. 对动态加载的图片友好:利用 AutoWebp 组件加载 img 标签时,

  3. 兼容性好

其他

文件格式信息表

压缩比例是测试采用 convert.py 从 https://random.imagecdn.app 随机取样10 张 png 和 10 张 jpg 进行转换后统计得出结果。

MIME

后缀

是否考虑支持

回退格式

压缩率(转为 webp 后减少的体积大小)

image/png

__p.webp

png

57.10%

image/jpg

__j.webp

jpg

50.50%

image/jpeg

__je.webp

jpeg

50.50%

image/bmp

__b.webp

bmp

-

image/x-icon

__i.webp

ico

-

image/vnd.radiance

-

hdr

-

image/gif

-

gif

-

image/raw

-

raw

-

image/tiff

-

tif

-

application/postscript

-

eps

-

image/bmp

-

dib

-

action

  • 使用方的角度应该在什么场景下如何使用

  • 为什么cdn上加入尺寸限制的后缀后webp反而比jpeg更高

https://fp.yangcong345.com/community/668bdff872e00900016a70c5-IMG_5803-acad5478f019fa6c42c4ccb12c309548__je.webp?x-tos-process=image/resize,m_fill,w_300,h_300&x-bce-process=image/resize,m_fill,w_300,h_300

24.7K

https://fp.yangcong345.com/community/668bdff872e00900016a70c5-IMG_5803-acad5478f019fa6c42c4ccb12c309548__w.jpeg?x-tos-process=image/resize,m_fill,w_300,h_300&x-bce-process=image/resize,m_fill,w_300,h_300

13K

https://fp.yangcong345.com/community/650f8e725fd8fd000163bb50-IMG_20240708_125946_690-25605edfb4849ddb6e434641feb27a16__w.jpeg

54K

https://fp.yangcong345.com/community/650f8e725fd8fd000163bb50-IMG_20240708_125946_690-25605edfb4849ddb6e434641feb27a16__je.webp

16.8K

https://fp.yangcong345.com/community/650f8e725fd8fd000163bb50-IMG_20240708_125946_690-25605edfb4849ddb6e434641feb27a16__je.webp?x-tos-process=image/resize,m_fill,w_300,h_300&x-bce-process=image/resize,m_fill,w_300,h_300

24K

https://fp.yangcong345.com/community/650f8e725fd8fd000163bb50-IMG_20240708_125946_690-25605edfb4849ddb6e434641feb27a16__w.jpeg?x-tos-process=image/resize,m_fill,w_300,h_300&x-bce-process=image/resize,m_fill,w_300,h_300

24.6K