写博客是为了和过去以及未来的自己对话
“对任何渴望进步的人来说,写博客/文章/回答对自己的成长帮助都是巨大的。坚持写作的那一刻起,就已经开始受益。”
随笔 - 21,  文章 - 0,  评论 - 0,  阅读 - 6569

对于响应头中没有指定缓存策略的资源(图片,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 来优化渲染时机。

  • 异步操作:如果某些操作不需要立即执行,可以使用 setTimeoutPromise 来将这些操作推迟到主线程空闲时,以减少对渲染的阻塞。

总结

  • 背景图片的显示:在 ready 中修改 background 后,背景图片的加载是异步进行的,但显示依赖于浏览器的重绘。
  • JavaScript 执行影响:如果 ready 中的 JavaScript 代码执行时间较长,可能会延迟背景图片的显示,直到 JavaScript 执行完成并释放主线程。
  • 避免阻塞:为了确保背景图片及其他渲染任务及时显示,应尽量避免在 ready 中执行耗时的同步操作。
posted on   sxb_sunday  阅读(265)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示