图片渲染优化
以前谈过许多次图片问题。也给出了几种方案。在实际使用中这几种无疑是可行而且方便的:
- loading
- connection API +
promise.all()
异步加载图片 - 骨架屏
- 懒加载 + 占位图
但是在电商场景下,第一种方案是不可以的:我们不能为了一张图片而放弃整个内容对用户的正常展现。(尤其是这个图片还只是个背景图)
第二种方案在首页是有奇效的,但是笔者觉得限制太多 —— 如果对于“通用型”的方案来说,骨架屏似乎更适合中大型项目。
这个方案当时提出的场景是:在我校的实验系统首页会有一个超大的轮播图,但是它的作用只是展示和宣传。很显然,这个轮播图并不需要所有的图片都能展示出来,但是如果放任不管,在网络情况稍差的时候会有空白甚至闪白的问题出现。这是不好的!所以用connection
API检测当前网络情况以决定展示轮播图还是一张图片。
最后一种方案应该是现在比较常用的。占位图还可以换做一个固定宽高的、有背景颜色的div
,减少一次图片的请求(以前去哪儿首页就这样做的)。
但是它的缺陷在于:如果图片太大,长时间的loading也会让用户觉得烦躁!
不做处理是不可能的。长时间的白屏或者闪白估计会让产品发疯。。。
雪碧图
CSS spirte是个好东西,它能大幅减少 http 开销。spirte + prefetch
的组合可以让优化 http 请求的同时让其提早被加载:
prefetch是对浏览器的“暗示”:将来可能需要某些资源,但由浏览器决定是否加载以及什么时候加载这些资源。它的优先级比较低。
spirte的原理是将整个整个页面需要的图片都放到一张图中,通过transform
移动到需要展示的地方。但还是那句话:如果有许多体积太大的图片,会让雪碧图的加载异常困难,从而带来不好的用户体验。
懒加载
刚进入网页页面就会有大批量的图片资源加载,这会间接影响页面的加载,增加白屏加载时间,影响用户体验。因此,我们的诉求就是不在可视化窗口内的图片不真正加载,尽可能减少本地带宽的浪费和请求资源的数量。
懒加载的优势很明显:
- 减少带宽资源消耗,减少不必要的资源加载消耗。
- 防止并发加载图片资源导致的资源加载阻塞,从而减少白屏时间。
实现简单的懒加载
实现的方式有两种:
- 通过
scroll
事件来监听视窗滚动区域实现。该方法兼容性好,绝大多数浏览器和WebView都兼容支持。 - 通过
IntersectionObserver
API观察DOM是否出现在视窗内,该方法优点在于调用简单,只是部分移动端兼容没有上一种方式好。
两种形式都是在观察当前DOM是否出现在了可视窗口内,如果出现的话就将data-src
中的图片地址赋值给src,然后开始加载当前的图片。
笔者之前专门研究过 预加载和懒加载(点击查看文章),在其中封装了一个函数可供调用。但那是通过监听 scroll 的方式。
事实上,浏览器提供的IntersectionObserver
API要更方便一些:
大图片渲染优化
你也许见过类似这张图:
普通图片加载方式和后来被提出的jpg渐进式加载、png交错式加载相比简直不值一提。
事实上,渐进式加载在实际使用中更加常见 —— 笔者推荐jpeg格式的渐进式加载方式:你不必完整的下载完毕图片,就可以看到图片的内容了。
没错它是一种由模糊到清晰的加载方式。
看到好多文章用JS模仿这种方式加载图片。但是…你把图片产生的 http 消耗转换为单线程js阻塞。结果是用户看到页面/可交互时间变长了。这样好么?
用 photoshop 生成图片时有个“存储为web所用格式”,然后,其中那个连续勾选就是渐进式JPEG图片了:
切记!需要勾选那个转换为sRGB选项,在某些浏览器下,图像设置为CMYK会出现一些问题!
FireWorks等图像软件也是有类似的输出设置的。
渐进式图片的优缺点
- 渐进式图片一开始大小框架就定好,不会像基本式图片一样,由于尺寸未设定而造成回流 —— 提高的渲染性能;
- 渐进式图片也有不足,就是吃CPU吃内存。
长图渲染优化
在电商场景中,最难受的其实是商品详情页面,这里运营可能会配置一些商品的详细描述图文。不仅对图片的质量会比较高,同时图片也会非常长。那么很显然,我们并不可能说直接拿到图片就显示在页面上,如果用户的网速比较慢的情况下,页面上就会直接出现一个很长的白条,或者一张加载失败的错误图。这些很明显不是我们想要的结果。
怎么办呢?
笔者研究了我司的图片处理工具:当你点开看大图时,会发现只显示了图片的一部分,或者在大图区域第一次只展示最顶部一小部分的内容。
依照这个思路,我们可以做相应的切图优化,将一张长图分成多个等比例大小的多张图块,来进行一个分批渲染调优,减少单次渲染长图的压力。
这个步骤显然是后端处理的 ——拿Node来说,你可以下载 gm
1 或是 ImageMagic
2
gm 中提供了用于剪裁的函数:
ImageMagic 更加强大,不仅可以剪裁图片,还支持 图片格式转换 和 图片识别 等功能!
处理完成后可以存放在专门的目录下,然后监听 scroll
或 IntersectionObserver
API在用户下滑时不断请求“下一段”图片。
图片异常处理
图片的异常分为两种:加载异常 和 渲染异常。
加载异常
加载异常就是指“由于各种原因导致的图片请求失败”。因为图片属于资源,资源的异常会触发一个 Event
接口的 error
事件,这些 error 事件不会向上冒泡到 window,但能被捕获。而window.onerror
不能监测捕获。
但可以被addEventListener
捕获:
渲染异常
渲染异常其实就是我们说的“页面上看不到图片、显示空白”。我们通常需要做的就是“降级处理” —— 比如给出一个失败的提示,或者给出降级的图片(比如用z-index
加持的background、伪元素加持下的图片/SVG)。
切记!如果你用了loading做加载优化,不要让loading一直存在!
- gm官网:http://www.graphicsmagick.org/ ↩︎
- ImageMagic官网:https://imagemagick.org/ ↩︎