饿了么 App 中新零售项目主要是以图片展示为主,引导用户点击轮播广告栏或者店铺列表进入指定的商品页面,因此页面中包含了大量图片,如搜索框下面的轮播广告栏、中部的促销栏以及底部的店铺列表,这些区域中都有大量的展示图片。因此图片的加载速率直接影响页面的加载速度。下面将从图片加载存在的问题和原因、解决方案两个方面来阐述如何优化新零售图片的加载。
本文所有数据及图片都是通过 Charles 模拟 256 kbps ISDN/DSL 网络环境获取到的。在本案例中只考虑位图,因此文本中提及的图片都是指位图而非矢量图。
图片加载存在的问题和原因
问题一:启动页面时加载过多图片
图1: 新零售图片请求瀑布图
问题原因分析:如上图所示,页面启动时加载了大约 49 张图片(具体图片数量会根据后端返回数据而变化),而这些图片请求几乎是并发的,在 Chrome 浏览器,对于同一个域名,最多支持 6 个请求的并发,其他的请求将会推入到队列中等待或者停滞不前,直到六个请求之一完成后,队列中新的请求才会发出。上面的瀑布图中,在绿色的标记框中,我们看到不同长度的白色横柱,这些都是请求的图片资源排队等待时间。
问题二:部分图片体积过大
图2. 顶部轮播图中的一张图片加载图
问题原因分析:如图 1,红框中是搜索框下部的轮播广告中的一张图片,通过图 2 可以看到,该图片主要耗时在 Conent Download
阶段。在下载阶段耗时 13.50s。而该请求的总共时间也就 13.78s。产生该问题的原因从图 1 也能看出一些端倪,该图片体积 76.2KB
,图片体积过大,直接导致了下载图片时间过长。
前端解决方案
针对问题一的解决方案
由于新零售首页展示展示大量图片,其实在这大约 49 张图片中,大部分图片都不是首屏所需的,因此可以延迟首屏不需要的图片加载,而优先加载首屏所需图片。这儿首屏的含义是指打开新零售首页首先进入屏幕视窗内的区域范围。
判断图片是否是首屏内图片,首先想到的肯定是通过 getBoundingClientRect 方法,获取到图片的位置信息,判断其是否在 viewport
内部。可能的代码如下:
const inViewport = (el) => {
const rect = el.getBoundingClientRect()
return rect.top > 0
&& rect.bottom < window.innerHeight
&& rect.left > 0
&& rect.right < window.innerWidth
}
但是在项目中,我们并没有采用该方案来判断是否在首屏,其原因在于,只有当 DOM 元素插入到 DOM 树中,并且页面进行重排和重绘后,我们才能够知道该元素是否在首屏中。在项目中我们使用了 v-img 指令(新零售项目使用该指令对图片进行加载、并且将 hash 转换成 Url。项目已开源,在符合需求前提下欢迎使用),在 Vue 指令中包含两个钩