web

图片资源加载与渲染

Posted by wml on October 26, 2018

背景

前两天测试妹纸拿着手机跑来,一个之前同事做的内嵌app的h5搜索结果页,在原生输入框搜索完关键字后,反复刷新,对着charles上的结果理直气壮和我说,为啥这个第三方上报,刷了一次就不上报了?==emmmm…哪有人这么无聊这么刷?(当然这句话我只敢在心里bb)果然测试妹纸上辈子都是被程序猿折了翅膀的天使。。。

既然测试妹纸提出来了,秉持着认真负责的态度,也是要一查究竟的。查看源码,第三方上报是通过图片img属性src发起的。

/* some code */
var img = new Image();
img.src = srcs[i];

难道是这货缓存导致的?一清缓存,果然又可以了。。。img缓存后就不再发起请求了?那图片资源的加载机制和渲染又是什么情况呢?针对这些问题,开始今天的实践之旅。

正文

先来看下浏览器的工作机制,下面这张图是网上找的: img

浏览器加载一个HTML页面后的操作:

  • 解析HTML —> 构建DOM树
  • 加载样式 —> 解析样式 —> 构建样式规则树
  • 加载javascript —> 执行javascript代码
  • 把DOM树和样式规则树匹配构建渲染树
  • 计算元素位置进行布局
  • 绘制

那图片资源的加载顺序又是怎样的呢?

  • 解析HTML【遇到<img>标签加载图片】 —> 构建DOM树
  • 加载样式 —> 解析样式【遇到背景图片链接不加载】 —> 构建样式规则树
  • 加载javascript —> 执行javascript代码
  • 把DOM树和样式规则树匹配构建渲染树【遍历DOM树时加载对应样式规则上的背景图片】
  • 计算元素位置进行布局
  • 绘制【开始渲染图片】

说明:DOM树和样式规则树匹配构建渲染树时,只会把可见的元素和对应的样式规则结合一起产出到渲染树,也就是说有不可见元素,当匹配DOM树和样式规则树时,若发现一个元素的对应的样式规则上有display:none,浏览器会认为该元素是不可见的,因此不会把该元素产出到渲染树上。

下面举一些栗子:

display:none
<style>
.img {
    background-image: url('./test3.png');
}
</style>

<img src="./test.jpg" alt="" style="display: none">
<div class="img" style="display: none"></div>

img

图片资源如上;上面代码设置了display:none的元素,图片不会渲染,但依旧会加载,包括背景图片。根据图片加载机制,在构建dom树时,解析HTML遇到img标签则加载该图片资源,遇到下面的div便签时也构建到dom树上,在遍历dom树,匹配样式规则树时,加载对应的样式规则,发现background-image则加载该图片,但由于样式规则上有display:none,则不把不可见元素产出到渲染树上。绘制时,渲染树没有该元素,也就不会绘制该图片。

父元素display:none
<style>
.img {
    background-image: url('./test3.png');
}
</style>

<div class="dom" style="display: none">
    <img src="./test.jpg" alt="">
    <div class="img"></div>
</div>

img

设置了display:none的父元素,图片资源会加载,但背景图片不会加载。在匹配DOM树和样式规则树时,发现该父元素节点上有display:none样式规则,浏览器则认为该元素是不可见的,所以不会继续遍历不可见的子元素,把子元素输出到渲染树上,因此也不会加载该元素中子元素的背景图片。

重复图片
<img src="./test.jpg" alt="">
<img src="./test.jpg" alt="">
<img src="./test.jpg" alt="">

img

重复图片路径图片只加载一次;浏览器请求资源时,都会先判断是否有缓存,先加载的图片会存储到浏览器缓存中,后面再次请求同路径图片时会直接读取缓存中的图片。这也是前面提到的场景中,重复刷新不会上报的原因,src中的资源在第一次加载中加载完成后,缓存在浏览器中,第二次图片加载时,遇到该地址会先从缓存里面取,所以也就不会再重复请求这个资源了。解决办法就是在src后面加个时间戳。

值得一提的是,这种图片加载的方式通常会用于一些统计信息,因为和ajax相比,img src不关心结果的返回,也不存在跨域问题。

不存在元素的背景图片
<style>
.img {
    background-image: url('./test.png');
}
.img2 {
    background-image: url('./test3.png');
}
</style>

<div class="img"></div>

img

不存在元素的背景图片不会加载。不存在的元素不会产出到DOM树上,构建渲染树过程中遍历DOM树时无法遍历不存在的元素,因此不会加载图片,也不会产出到渲染树上。当解析渲染树时无法解析不存在的元素,不存在的元素自然也不会渲染。

在了解完图片加载和渲染机制后,就能更好的管理图片资源,避免不必要的浏览和提高用户体验,如图片占位和图片预加载等。eg:使用背景图片占位,使用base64格式的图片。因为背景图片加载的顺序在<img>标签后面,可能会在<img>标签图片加载完成后才开始加载,达不到想要的效果。