2023年7月17日发(作者:)
⽹站图⽚⽆缝兼容WebPAVIF前⾔WebP 格式发布已有⼗余年,但不少站点⾄今仍未使⽤,只为兼顾极少数低版本浏览器。⾄于去年发布的 AVIF 格式,使⽤的站点就更少了。然⽽图⽚往往是流量⼤户,与其费尽⼼机优化脚本体积,可能还不如转换⼀张⼤图带来的收益更多。据 统计,如今有 67% 的⽤户⽀持AVIF、95% 的⽤户⽀持 WebP。先进的格式触⼿可得,却因兼容性问题仍坚守 PNG、GIF 等古⽼格式,⽩⽩浪费⽹站流量,以及⽤户加载时间,实在浪费。事实上,对于同⼀个图⽚ URL,完全可为低版本浏览器使⽤⽼格式,为⾼版本浏览器使⽤新格式,从⽽实现⽆缝兼容。本⽂讲解后端和前端两种不同的实现⽅案。后端⽅案这是最简单也是最普及的⽅案,⽹上能搜到很多相关的⽂章。不过这其中存在诸多细节,⼤多数⽂章都未考虑全⾯。原理⽀持 WebP 的浏览器,HTTP 请求的
Accept 头会包含
image/webp 字符,后端可根据该特征返回 WebP 版图⽚;不⽀持的浏览器则没有该特征,后端返回原始图⽚。AVIF 同理,特征为
image/avif。由于 AVIF ⽐ WebP 更先进,因此需优先判断。实现由于图⽚解码和编码开销很⼤,因此格式转换通常离线完成,例如预先将
转换成
和
。这⾥有⼏个细节:如果 WebP ⽂件⽐原⽂件更⼤,那就没有必要保留 WebP ⽂件。AVIF 同理如果原⽂件本⾝就是 WebP ⽂件,那就不⽤再转 WebP 了。但可以尝试转 AVIF 版本,如果更⼩则保留如果原⽂件本⾝就是 AVIF ⽂件,那什么都不⽤做运⾏时的判断逻辑很简单,但也容易疏漏。以 nginx 为例,通常会这样配置:http { map $http_accept $_ext { ~image/avif .avif; ~image/webp .webp; default ''; } server { location / { add_header Vary Accept; try_files $uri$_ext $uri =404; } }}看起来好像没问题,但遇到这种情况就不对了:⽤户⽀持 WebP 和 AVIF,但后端只存在 WebP ⽂件。正常应该返回 WebP,但这⾥
try_files只尝试⼀次,找不到 $ 就返回原⽂件了。显然不对。正确应该
try_files 两次:http { map $http_accept $_avif { ~image/avif .avif; ~image/webp .webp; default ''; } map $http_accept $_webp { ~image/webp .webp; default ''; } server { location / { add_header Vary Accept; try_files $uri$_avif $uri$_webp $uri =404; } }}注意这⾥的逻辑顺序。即使⽤户不⽀持 AVIF 扩展名也不能直接返回空,否则就是在尝试原⽂件了。⾄于可能会重复尝试两次 WebP ⽂件,虽不优雅但也⽆⼤碍。此外,如果希望⽤户访问⽬录名时 URL 末尾能⾃动添加
/(例如访问
/blogs 时先重定向到
/blogs/),那么
try_files 还需添加
$uri/。演⽰⽀持 AVIF 的浏览器,返回的图⽚类型为
image/avif不⽀持 AVIF 但⽀持 WebP 的浏览器,返回的图⽚类型为
image/webp既不⽀持 AVIF ⼜不⽀持 WebP 的浏览器,返回的图⽚类型为
image/jpeg优点后端实现的⽅案显然通⽤性很好,前端⽆需修改即可⽣效,甚⾄前端不是浏览器都没关系,只要遵循 HTTP 的 Accept 规范即可。缺点 1由于同⼀个 URL 会返回不同的内容,如需通过 CDN 加速,则需配置
Vary: Accept 响应头,以确保代理服务器能根据不同的
Accept 请求头缓存相应的内容。然⽽⽬前 CloudFlare 免费版却⽆视
Vary,开启这个功能意味低版本浏览器显⽰不了图⽚!缺点 2不同格式的图⽚,即使像素完全相同,但⽂件数据显然是不同的。假如业务依赖⽂件数据,例如校验⽂件 Hash,那么显然会失败,从⽽导致业务损坏。对于这个问题,有两种缓解⽅案:1. 判断 相关的请求头,对于有能⼒读取⽂件数据的请求,则不考虑升级2. 通过⿊⽩名单机制,只允许或不允许某些图⽚升级第 1 种⽅案更通⽤,但 Fetch Metadata 只有较⾼版本的浏览器才⽀持,并且某些特殊场合仍可能存在问题。第 2 种⽅案更稳定,但需整理⽂件列表并在后端维护,显然很⿇烦。前端⽅案如果⽹站搭建在虚拟空间、GitHub Pages 等这类⽆法修改配置的后端,或者使⽤了 CloudFlare 免费加速服务,那只能在前端实现。原理前端升级图⽚有多种⽅案。最容易想到的就是⽤ JS 在线解码⾼版本图⽚。当初 WebP 发布时我对此颇有兴趣,,并且能⾃动替换⽹页中的图⽚元素,看起来就像原⽣⽀持⼀样。但实际应⽤后发现并不理想,⼀是不⽀持 CSS 图⽚(实现很⿇烦),⼆是解码性能差。虽然使⽤了Alchemy 编译技术(LLVM → ActionScript ByteCode),但性能相⽐原⽣仍差⼀⼤截。最终放弃了这个⽅案。尽管后来有更先进的计算⽅案,例如 WebWorker、 / WebAssembly、SIMD 等,但仍然达不到原⽣性能,并且代码体积很⼤。所以在线解码的⽅案仍不考虑。直到另⼀个⿊科技的出现,使得前端升级图⽚变得⾮常容易,并能覆盖⽹页中所有图⽚,那就是
Service Worker。它能拦截当前站点产⽣的所有请求,并能控制返回结果,相当于⼀个反向代理服务。于是我们可以在 Service Worker 中判断
Accept 请求头,然后代理到相应的 URL。实现得益于 Service Worker 强⼤的功能,图⽚除了格式升级外还能玩出很多「骚操作」,例如可将图⽚部署在免费的图床、相册上,使⽤时根据清单中的地址进⾏反向代理,从⽽可将图⽚流量降低到 0!并且可准备多个图⽚ URL 做冗余备份,以及完整性校验等等。这个思路之前在 尝试过,不过实际应⽤起来似乎并不容易。最近我重新整理这个思路,并实现了⼀个⼯具:,它可以⾃动⽣成清单⽂件,记录原⽂件的备⽤ URL 列表、Hash 值、是否⽀持WebP/AVIF 升级等信息。演⽰Service Worker 不仅将 JPEG 升级成 AVIF 版本,甚⾄从免费 CDN 加载,将流量开销「优化」到了零!从浏览器界⾯上看,和直接访问图⽚⼀模⼀样,但实际上该图⽚是由 Service Worker 提供的。具体原理和细节可 。优点后端⽆需任何修改,⽆论是普通的服务器、CDN 还是虚拟空间都可以。前端只需引⽤⼀个脚本开启 Service Worker,⽆需修改业务逻辑。由于 Service Worker 运⾏在前端,因此能获取到更详细的 ,从⽽可实现更智能的策略。此外,即使要配置⿊⽩名单,只需通过⼀个清单⽂件即可实现,⽐修改后端服务配置⽅便很多。缺点如果⽤户的浏览器不⽀持脚本,或者根本不是浏览器访问,那么 Service Worker 显然⽆法运⾏,图⽚升级功能⾃然就失效了。这种情况只能使⽤后端⽅案。
发布者:admin,转转请注明出处:http://www.yc00.com/news/1689567427a266859.html
评论列表(0条)