Web图片资源的加载与渲染时机

此文研究页面中的图片资源的加载和渲染时机,使得我们能更好的管理图片资源,避免不必要的流量和提高用户体验。

浏览器的工作流程

要研究图片资源的加载和渲染,我们先要了解浏览器的工作原理。以Webkit引擎的工作流程为例:

webkitflow

从上图可看出,浏览器加载一个HTML页面后进行如下操作:

  • 解析HTML —> 构建DOM树

  • 加载样式 —> 解析样式 —> 构建样式规则树

  • 加载javascript —> 执行javascript代码

  • 把DOM树和样式规则树匹配构建渲染树

  • 计算元素位置进行布局

  • 绘制

从上图我们不能很直观的看出图片资源从什么时候开始加载,下图标出图片加载和渲染的时机:

  • 解析HTML【遇到<img>标签加载图片】 —> 构建DOM树

  • 加载样式 —> 解析样式【遇到背景图片链接不加载】 —> 构建样式规则树

  • 加载javascript —> 执行javascript代码

  • 把DOM树和样式规则树匹配构建渲染树【遍历DOM树时加载对应样式规则上的背景图片】

  • 计算元素位置进行布局

  • 绘制【开始渲染图片】

图片加载与渲染规则

页面中不是所有的<img>标签图片和样式表背景图片都会加载。

display:none

<style>
.img-purple {
    background-image: url(../image/purple.png);
}
</style>
<img src="../image/pink.png" style="display:none">
<div class="img-purple" style="display:none"></div>

图片资源请求如下:
display-none

设置了display:none属性的元素,图片不会渲染出来,但会加载。

原理

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

上面代码中,当解析HTML时会加载<img>标签元素上的图片。

当把DOM树和样式规则树匹配构建渲染树时,遍历DOM树上的元素,发现元素对应的样式规则上有background-image属性时会加载背景图片,但是因为这个元素是不可见元素(对应的样式规则上有diaplay:none),不会把该元素和它对应的样式规则产出到渲染树。

当绘制时因为渲染树上没有该元素,因此不会绘制该元素的背景图片。

<style>
.img-yellow {
    background-image: url(../image/yellow.png);
}
</style>
<div style="display:none">
    <img src="../image/red.png">
    <div class="img-yellow"></div>
</div>

图片资源请求如下:
display-none

设置了display:none属性元素的子元素,样式表中的背景图片不会渲染出来,也不会加载;而<img>标签的图片不会渲染出来,但会加载。

原理

正如上面所说的,当匹配DOM树和样式规则树时,若发现元素的对应的样式规则上有display:none,浏览器会认为该元素的子元素是不可见的,因此不会把该元素的子元素产出到渲染树上。

当构建渲染树遇到了设置了display:none属性的不可见元素时,不会继续遍历不可见元素的子元素,因此不会加载该元素中子元素的背景图片。

当绘制时也因为渲染树上没有设置了display:none属性元素,也没有改元素的子元素,因此该元素中子元素的背景图片不会渲染出来。

重复图片

.img-blue {
    background-image: url(../image/blue.png);
}
<div class="img-blue"></div>
<img src="../image/blue.png">
<img src="../image/blue.png">

图片资源请求如下:
repeat-image

页面中多个<img>标签或样式表中的背景图片图片路径是同一个,图片只加载一次。

原理

浏览器请求资源时,都会先判断是否有缓存,若有缓存且未过期则会从缓存中读取,不会再次请求。先加载的图片会存储到浏览器缓存中,后面再次请求同路径图片时会直接读取缓存中的图片。

不存在元素的背景图片

.img-blue {
    background-image: url(../image/blue.png);
}
.img-orange{
    background-image: url(../image/orange.png);
}
<div class="img-orange"></div>

图片资源请求如下:
no-image

不存在元素的背景图片不会加载。

原理

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

伪类的背景图片

.img-green {
    background-image: url(../image/green.png);
}
.img-green:hover{
    background-image: url(../image/red.png);
}
<div class="img-green"></div>

触发hover前的图片资源请求如下:
class-image

触发hover后的图片资源请求如下:
class-image

当触发伪类的时候,伪类样式上的背景图片才会加载。

原理

触发hover前,构建渲染树过程中,遍历DOM树时,该元素匹配的样式规则是无hover状态选择器.img-green的样式,因此加载无hover状态选择器.img-green的样式上green.png图片。该元素是可见元素,因此会被产出到渲染树上,绘制时渲染的也是green.png

触发hover后,因为.img-green:hover的优先级比较高,构建新的渲染树过程中,该元素匹配的是有hover状态选择器,因此加载有hover状态选择器.img-green:hover的样式上的red.png图片。该元素是可见元素,因此会被产出到渲染树上,绘制时渲染的也是red.png

应用

占位图

当使用样式表中的背景图片作为占位符时,要把背景图片转为base64格式。这是因为背景图片加载的顺序在<img>标签后面,背景图片可能会在<img>标签图片加载完成后才开始加载,达不到想要的效果。

预加载

很多场景里图片是在改变或触发状态后才显示出来的,例如点击一个Tab后,一个设置display:none隐藏的父元素变为显示,这个父元素里的子元素图片会在父元素显示后才开始加载;又如当鼠标hover到图标后,改变图标图片,图片会在hover上去后才开始加载,导致出现闪一下这种不友好的体验。

在这种场景下,我们就需要把图片预加载,预加载有很多种方式:

  1. 若是小图标,可以合并成雪碧图,在改变状态前就把所有图标都一起加载了。

  2. 使用上文讲到的,设置了display:none属性的元素,图片不会渲染出来,但会加载。把要预加载的图片加到设置了display:none的元素背景图或<img>标签里。

  3. 在javascript创建img对象,把图片url设置到img对象的src属性里。

欢迎关注:Leechikit
原文链接:segmentfault.com

到此本文结束,欢迎提问和指正。
写原创文章不易,若本文对你有帮助,请点赞、推荐和关注作者支持。


Leechikit的专栏
记录自己前端职业之路的积累与成长
2.1k 声望
114 粉丝
0 条评论
推荐阅读
傻傻分不清的__proto__与prototype
小英童鞋认为Foo的原型对象是Foo.prototype,所以Foo会继承Foo.prototype的属性,调用Foo.combineName()相当于调用Foo.prototype.combineName(),但结果Foo.combineName()不是一个方法。

Leechikit50阅读 14.7k评论 13

还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库

XboxYan34阅读 2.4k评论 2

封面图
【关于Javascript】--- 正则表达式篇
基础知识一、元字符 {代码...} 二、量词 {代码...} 三、集合 字符类 {代码...} 四、分支 {代码...} 五、边界 开始结束 {代码...} 六、修饰符 {代码...} 七、贪婪模式和非贪婪模式js默认贪婪模式即最大可能的匹配...

Jerry35阅读 3k

CSS 如何设置自动滚动定位的“安全”间距?
欢迎关注我的公众号:前端侦探介绍两个和滚动定位相关的 CSS 属性:scroll-padding和 scroll-margin在平时开发中,经常会碰到需要快速定位的问题,比如常见的锚点定位 {代码...} 这样,在点击a标签时会自动定位到...

XboxYan31阅读 2.3k评论 2

封面图
CSS transition 小技巧!如何保留 hover 的状态?
欢迎关注我的公众号:前端侦探通常情况下,hover 是无法保存状态的。鼠标移入触发额外样式,一旦移出就还原了 {代码...} 这就意味着,如果需要保留hover的状态,可能就不得不借助JS了,比如下面是某某书院的首页...

XboxYan29阅读 3.6k评论 2

封面图
现代 CSS 之高阶图片渐隐消失术
在过往,我们想要实现一个图片的渐隐消失。最常见的莫过于整体透明度的变化,像是这样: {代码...} {代码...} 但是,CSS 的功能如此强大的今天。我们可以利用 CSS 实现的渐隐效果已经不再是如此的简单。想想看,...

chokcoco25阅读 2.1k

封面图
除了 filter 还有什么置灰网站的方式?
大家都知道,当一些重大事件发生的时候,我们的网站,可能需要置灰,像是这样:当然,通常而言,全站置灰是非常简单的事情,大部分前端同学都知道,仅仅需要使用一行 CSS,就能实现全站置灰的方式。像是这样,我...

chokcoco19阅读 1.7k评论 1

封面图
2.1k 声望
114 粉丝
宣传栏