移动 WEB 通用优化策略介绍(二)

算起来已经足足两个半月没有更新文章,这段时间过得比较忙:夜间跑步计划、卖房买房以及工作上各种事情都凑到一块了。实际上,最近也并没有忙到完全抽不出时间写博客这种地步,根本原因可能还是变懒了,这样不好。

几个月前,我决定开始写一系列有关「移动 WEB 通用优化」的文章,介绍「面向所有主流移动端浏览器(包括各种 APP 嵌入的通用 Webview)」的前端优化策略,本文是这个系列第二篇。

先来回顾下上篇文章最后的结论:

重要的 CSS、JS、JSON 数据直接内联在 HTML 中,头部禁止出现任何外链资源。同时,尽可能减少页面传输体积。

采用了这个策略的页面,理应能让用户在很短时间内看到主体内容,因为头部 CSS 和 JS 都内联了,不会阻塞浏览器渲染页面。在我们的认识里,浏览器会异步加载页面用到的图片,加载图片不会阻塞页面渲染,更不会阻塞 JS 执行。实际情况是这样吗?

本文主要讨论在移动 WEB 中,图片的加载给页面整体性能带来的影响以及优化策略。

我们知道,浏览器的 DOMContentLoaded 事件会在主页面加载并解析完成之后触发,不会等页面样式、图片、iframe 等子资源加载完。以下是 MDN 对它的描述:

The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.

但在实际测试中,移动端完全相同的页面,加载与不加载图片对 DOMContentLoaded 触发时机的影响却很大。以下是我们在某个移动产品中,将图片延迟加载后的 DOMContentLoaded 时间对比,可以看出明显变化:

dom ready time

我们只是将页面所有图片(大约十几张)进行延迟加载,就让 DOMContentLoaded 事件提前 250 毫秒触发。这是我之前没有意料到的,移动设备在网络、CPU、内存等方面的性能与 PC 相比差距很大,很多 PC 上可以忽略的问题,在移动端必须重视起来。

移动 WEB 要做好图片优化,无外乎两点:控制图片大小控制图片加载

控制图片大小

图片高宽越大,意味着需要越多的网络开销。常见图片格式都经过了高度压缩,尺寸越大的图片还意味着浏览器在解码过程中需要耗费更多 CPU,解码之后的位图需要占用更多内存。在移动端,我们更应该关注图片大小。

根据 DPR(window.devicePixelRatio,设备像素比)选择合适的图片尺寸。现在的手机基本上都是高清屏,如果一味追求让图片更小而使用单倍图也不现实。这一点上,最佳实践是根据产品特性,结合用户 DPR 分布情况来选择合适的尺寸。例如在我们某个产品中:图片加载速度比图片质量更重要;用户 DPR 分布前三是:2、3、1.5。我们最终使用了 1.5 倍图,并且在图床缩放图片时,加了一点点锐化效果。最终图片体积很小,质量也尚可接受。

处理好响应式图片(Responsive Image)。移动上很多图片宽度不是固定像素值,例如通栏 Banner 图片的宽度是跟着设备走的。对于这种场景,使用 JS 获取设备宽度,拼出最适合当前设备的图片尺寸,交给图床进行缩放,无疑能在图片体积和质量上找到最佳平衡点。但这种做法并不可取,移动设备宽度各式各样,如果裁图规格太多,容易降低 CDN 缓存命中率。图床实时处理完图片再分发到 CDN 更耗时,在移动端让图片命中 CDN 缓存也很重要。处理响应式图片的最佳实践是根据用户屏幕尺寸分布,制定出几档裁图规则,页面根据用户设备宽度使用最合适的档位,并对重要的图片(例如头部焦点图)提前预热 CDN。

有一项名为 HTTP Client Hints 的技术,通过新增的 HTTP 请求头部字段,使得图床可以优雅地返回最合适的图片。目前这项技术在移动端尚未普及,详情请查看 HTTP Client Hints 介绍

使用 WEBP 格式。有一种减少图片体积的灵丹妙药 —— 使用压缩比例更高的 WEBP 格式。《移动端图片格式调研》这篇文章详细地对比了各种移动端图片格式及各自适用场景。对于 WEBP 的最佳实践是只要浏览器支持就用,虽然 WEBP 解码慢于 JPG,但在同等图片质量下,WEBP 体积通常比 JPG 小很多。

要判断浏览器是否支持 WEBP,可以检查 HTTP 请求头部字段 Accept 的值是否包含 webp。例如这是 Chrome 给图片请求加的 Accept

Accept: image/webp,image/*,*/*;q=0.8

不是所有支持 WEBP 的浏览器都会这样处理,可以针对这种情况使用特性检测:

var webpImg = new Image;
webpImg.onload = function () {
    if(webpImg.width == 1){
        cookie('env_webp', 1); //cookie 方法需要自己实现
    }
};
webpImg.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAgA0JaQAA3AA/vv9UAA=';

这段示意代码的原理是:用 JS 加载 WEBP 图片,如果能触发 onload 并获取到宽度,说明当前浏览器支持 WEBP。

控制图片加载

我在《AMP,来自 Google 的移动页面优化方案》一文中写到:「将图片、视频等标签和第三方功能换成 AMP Components 后,AMP Runtime 可以自动处理延迟加载、按需加载等逻辑,确保页面首屏性能」。在移动浏览器打开网页,经常能感觉到明显的卡顿。造成卡顿的原因除了页面 DOM 结构复杂、CSS 过多地触发 Layout/Paint/Composite、存在复杂 JS 逻辑等等,也可能是没有控制图片的加载时机。

通常浏览器会并发加载 6 个同域名图片,如果做了域名散列,那很可能在打开页面后的短短几秒内,几十个图片都在加载。这些连接带来的 TCP、带宽、CPU、内存等开销,很容易让页面卡顿。所以在移动端,我们要让图片加载变得可控。

按需加载图片。在 PC 端,我们基本都会做图片 Lazy Load,这个优化策略在移动端同样适用。由于移动端性能有限、带宽昂贵,Lazy Load 更为重要。实际上不光是图片可以做 Lazy Load,页面所有资源包括 DOM 节点都应该做成按需加载。通常在移动端,我们只加载页面可视区域及其下方一定距离内的资源。

顺序加载图片。在 PC 端,由于硬件性能和带宽足够,并行加载更多的图片通常是最好的选择。而在移动端,人为控制图片加载顺序,例如使其从上到下、从左到右逐个加载,有时可以带来更好的体验。

不要在页面滚定时加载图片。按需加载图片逻辑需要监听页面滚动事件,根据页面当前可视区域决定加载哪些图片。在移动端滚动页面本来就很耗费性能,如果这时候还要加载图片,非常容易造成页面卡顿。在页面滚定停止之后才开始载入图片,能有效减少这种卡顿。

好了,本文先就写这么多。还是老规矩,有任何问题和疑问欢迎留言讨论。Disqus 最近在国内经常无法加载,怎么处理你们都懂的。实在不行也可以给我发邮件,本站「关于」页面有我的邮箱。

本文链接:参与评论 »

--EOF--

Comments