对于响应头中没有指定缓存策略的资源(图片,js,css等),浏览器默认会缓存,当再次请求这些资源的时候,会优先从缓存中获取,缓存命中且在有效期内的话,则不会发出请求,缓存命中但超过有效期,这时候会和服务器协商缓存(此部分内容本文不做解析)。
默认缓存策略是启发式缓存,缓存有效时间的计算式:(请求时间 - 请求资源的修改时间)* 10%。
例如图片修改时间是2024/08/29 12:00:00,第一次访问该资源的时间是2024/08/30 12:00:00。那么时间之差就是24小时,浏览器会将该图片在磁盘中存储2.4小时。也就是说从2024/08/30 12:00:00 到 2024/08/30 14:24:00 期间的对于该图片的访问,浏览器都会直接从缓存中获取而不会向服务器发出请求。
浏览器能从缓存中取到图片的时候,则画面会在renderTree(渲染树)准备好后立刻渲染,否则会等待图片加载完之后(服务器返回数据)才进行画面渲染。
js的执行会阻塞画面的渲染(本质是阻塞样式树的生成,因为js会修改样式,避免因为反复修改样式而造成不必要的性能开销),而遇到js代码的时候,浏览器会首先进行一次画面渲染(目的为了当前的样式和布局是确定的,因为js可能会操作样式或布局),
因此我最近遇到一个问题是,没有预加载图片,而背景图片(background-image)是通过js代码在body最后加载的,这就是导致执行这个js代码的时候,画面提前做一次渲染,但是没有显示图片,js代码里面指定了图片资源位置,当执行完毕画面再次渲染的时候,将图片渲染出来了。从画面上看就是,画面先表示出来了,但是过了一瞬间,图片才表示。使用图片预加载之后,发现这样的现象没有了,难道说js指定背景图片的时候(这时候因为js执行还没结束,所以画面应该不会渲染),缓存命中的情况,画面不需要渲染可以直接显示出来吗?是因为浏览器不需要回流,只要重绘吗?但是缓存没有,从服务器加载的情况(这个动作是后台进行的,并不会阻塞js的执行)下,会等待下一次画面渲染才能表示出图片。个人推测是,缓存直接获取的时候,因为不需要回流,所以直接显示,不能获取的时候,浏览器不会等待图片的加载,js继续执行了,那么这时候即使图片加载好了(假设即使图片很小网速很快),浏览器也不会反过去重绘图片,而是等待下次渲染的时候一起搞定(浏览器其实很懒啊)。理解可能不对,后面弄明白了再回来补充
2024/08/31
重新查阅了一些博客以及在chatGPT的帮助下,发现我上面的解释有些不准确,把这两天查阅的结果做如下整理。(没有系统学过浏览器的工作原理,只能不断理解不断完善=.=)
- 首先CSS中的background属性和DOM元素img标签是不一样的
关于CSS background
属性中的背景图片和 <img>
标签的加载行为,有一些重要的区别和细节:
1. CSS background
属性中的图片加载
-
请求时机:当浏览器解析到含有
background
属性定义的CSS时,它并不会立即请求背景图片。通常情况下,浏览器会等到确定了该元素将被渲染到页面上之后(即确定该元素可见并且实际需要显示)才发起图片请求。这意味着背景图片的请求是与页面的布局和绘制密切相关的,通常是当浏览器确定需要渲染该元素时才会开始加载图片。 -
异步加载:背景图片的请求是异步进行的。浏览器不会等待背景图片加载完毕再继续渲染页面的其他部分。相反,浏览器会继续解析和渲染其他内容,同时后台加载背景图片。这种机制可以加速页面内容的呈现。
-
懒加载:如果某些元素不在当前视口范围内(例如在页面底部的某些元素),某些优化策略可能会延迟这些元素背景图片的加载,直到用户滚动到该元素的可视区域。这种策略通常通过浏览器的优化机制或开发者手动实现。
2. <img>
标签中的图片加载
-
请求时机:当浏览器解析到
<img>
标签时,通常会立即开始请求该图片资源,不需要等待页面完全布局完成。因此,浏览器在解析HTML的同时,一旦发现<img>
标签并读取其src
属性,就会立即发起图片加载请求。这意味着<img>
标签中的图片往往比background
属性中的背景图片更早发起加载请求。 -
阻塞行为:虽然
<img>
标签会立即请求图片,但图片的加载是异步的,不会阻塞页面的其他部分渲染。页面的文本和布局仍然会继续渲染,图片会在加载完成后显示出来。
对比总结:
-
CSS 背景图片:
- 解析到相关CSS规则时不一定立即发起请求,而是通常在需要渲染该元素时才请求。
- 背景图片是异步加载的,不会阻塞页面渲染。
- 某些情况下,背景图片的加载会被延迟,直到该元素即将进入视口。
-
<img>
标签图片:- 解析到
<img>
标签时立即发起请求。 - 同样是异步加载,不会阻塞页面渲染。
- 但是
<img>
图片资源的请求通常比背景图片更早,因为它是在HTML解析阶段发起的,而背景图片是在CSS渲染阶段。
- 解析到
两者的加载机制不同,主要是因为它们所处的上下文不同:<img>
是HTML的一部分,而 background-image
是CSS的一部分,因此加载和渲染的时机会有所不同。
- 所以之前的理解有误,背景图片的请求不是理解发出的。而是在画面渲染过程中,发现包含背景图片的CSS样式确实被应用在DOM元素中的时候,才会请求(等待请求返回的过程是异步的)。
背景图片的加载和渲染通常不会导致回流(reflow),但可能会引发重绘(repaint)。下面是对这两者的解释及背景图片的影响:
1. 回流(Reflow)
-
定义:回流是指当页面布局或尺寸发生变化时,浏览器需要重新计算元素的位置和大小。回流会影响页面的布局树,需要重新布局和渲染页面的部分或全部内容。
-
触发条件:通常由元素的几何属性(如宽度、高度、边距、边框、内边距等)的变化或添加/移除DOM元素引起。
-
与背景图片的关系:背景图片的加载和渲染一般不会触发回流。这是因为背景图片本身不影响元素的尺寸或布局,背景图片只是渲染在元素的背景上,不改变元素的几何属性。因此,即使背景图片在布局计算后加载完成,也不会导致页面重新布局。
2. 重绘(Repaint)
-
定义:重绘是指当元素的外观发生变化(但不影响布局)时,浏览器需要重新绘制元素的部分或全部内容。重绘只会重新绘制元素的外观,而不涉及布局的重新计算。
-
触发条件:重绘通常由元素的视觉属性变化引起,比如颜色、背景、阴影等的变化。
-
与背景图片的关系:背景图片的加载可能会触发重绘。当背景图片加载完成后,浏览器需要将图片绘制到元素的背景中。这一操作虽然不会影响元素的布局,但会导致元素或其包含的区域被重新绘制。
总结
-
背景图片不会导致回流,因为它们不影响元素的布局或几何属性。背景图片的加载通常发生在布局计算完成之后,不会引起页面布局的重新计算。
-
背景图片可能会导致重绘,因为当图片加载完成后,浏览器需要将其绘制到元素的背景中。重绘是一个较轻的操作,只涉及视觉上的更新,不会像回流那样影响性能显著。
总体来说,背景图片的加载和渲染对于页面性能的影响相对较小,特别是相比于回流。通过合理使用背景图片和优化加载策略,可以有效减少重绘次数,从而提升页面的渲染性能。
最后是对我之前遇到问题的补充解释:
在浏览器处理页面时,JavaScript 的执行会阻塞页面的渲染(包括背景图片的显示):
1. JavaScript 的执行与渲染的关系
-
单线程模型:浏览器的JavaScript引擎是单线程的,这意味着它在任何时间点只能执行一件事情。当 JavaScript 代码执行时,浏览器的主线程被占用,不能同时进行其他任务(如渲染或处理用户交互)。
-
渲染队列:当 JavaScript 在执行过程中对页面进行了修改,例如修改
background
图片路径,浏览器会将这些渲染任务(如重绘)放入渲染队列中,但不会立刻执行渲染任务。渲染任务通常在 JavaScript 执行完成后,才会由浏览器来处理。这就是为什么画面先表示出来了,但是过了一瞬间,图片才表示。这一瞬间是js代码的执行时间 - 重绘周期:浏览器的渲染过程是基于帧的,一般每秒渲染60帧(每帧约16.7毫秒)。在每个渲染周期中,浏览器会处理所有需要更新的部分,包括因背景图片加载完成而引起的重绘。浏览器会在合适的时机进行重绘,以显示新的内容。
2. 背景图片加载与显示的时机
-
修改背景图片路径:在
ready
事件中修改background
图片路径会触发图片的加载请求。如果图片很快加载完成,浏览器会将其放入渲染队列,准备在下一个重绘时显示出来。 -
等待 JS 执行完成:然而,如果
ready
中的 JavaScript 代码仍在执行,浏览器的主线程被占用,渲染任务(包括重绘背景图片)就会被延迟执行。只有在 JavaScript 代码执行完成并释放主线程后,浏览器才能处理渲染队列,执行重绘,最终显示背景图片。
3. 现实中的表现
-
通常不会有明显延迟:如果
ready
中的 JavaScript 代码执行时间很短,那么图片的显示延迟几乎不可察觉。浏览器会在代码执行完成后的下一次重绘中显示图片。 -
长时间执行的 JavaScript:如果
ready
中的 JavaScript 代码执行时间较长(例如有复杂的运算或大规模的DOM操作),那么背景图片的显示可能会被延迟,直到 JavaScript 执行完成。
4. 优化建议
-
避免长时间阻塞:尽量避免在
ready
中执行耗时的操作。如果确实需要,可以考虑将其拆分成异步任务或使用requestAnimationFrame
来优化渲染时机。 -
异步操作:如果某些操作不需要立即执行,可以使用
setTimeout
或Promise
来将这些操作推迟到主线程空闲时,以减少对渲染的阻塞。
总结
- 背景图片的显示:在
ready
中修改background
后,背景图片的加载是异步进行的,但显示依赖于浏览器的重绘。 - JavaScript 执行影响:如果
ready
中的 JavaScript 代码执行时间较长,可能会延迟背景图片的显示,直到 JavaScript 执行完成并释放主线程。 - 避免阻塞:为了确保背景图片及其他渲染任务及时显示,应尽量避免在
ready
中执行耗时的同步操作。
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现