简单谈谈浏览器渲染页面过程的几个问题

本文深入解析浏览器渲染页面的步骤,包括HTML解析、DOM树构建、CSSOM树关联、布局计算及渲染过程。探讨了外部样式表与脚本加载对渲染的影响,以及如何优化以提升页面加载速度。

浏览器渲染页面,简单的说,主要是以下几步

  1. 解析html
  2. 构建DOM树(第1,2一般是同时执行)
  3. 加载外部样式表
  4. 解析外部样式表为CSSOM树
  5. 将DOM树和CSSOM树关联在一起形成渲染树(render树),有哪些节点,各个节点的CSS定义以及他们的从属关系。
  6. 布局,根据渲染树计算页面元素的大小,位置等信息
  7. 渲染,将布局计算好的元素渲染到页面上

几个问题

  1. 解析html的时候,是从上到下解析的,如果遇到外部样式表(link),会新开一个线程去下载,并不会影响DOM树的构建,也就是说,解析html,构建DOM树,以及加载css可以同时进行,因此将外部样式表放到head中,越早加载越好。另外是解析完外部样式表,才会将DOM和CSSOM关联再一起形成渲染树,所以css的link如果放到页面最下面,就算html已经解析完成,还是要等外部样式表加载完成,才会形成渲染树,很大几率会浪费时间。
  2. 解析html的时候,是从上到下解析的,如果遇到外部脚本(script),会停掉解析html的工作,先加载script内容,等加载完,并且script的内容执行完毕之后,才会继续解析html的工作,既然解析html的工作停止了,那页面渲染肯定不会继续执行,客户端用户看到的就是一片空白,所以如果将外部脚本放到html上部,会阻塞页面的渲染,因为阻塞了解析html的工作。另外,如果script中需要操作DOM,此时DOM树还没有构建完成,因此找不到DOM元素,就会报错。
  3. script的标签上,可以添加async和defer属性。
    ————
    defer:用于开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。
    1)defer只适用于外联脚本,如果script标签没有指定src属性,只是内联脚本,不要使用defer
    2)如果有多个声明了defer的脚本,则会按顺序下载和执行
    如果有两个script标签:scriptA(文件大),scriptB(文件小)都有defer属性,A在B之前,浏览器并发的请求一般为6个,因此可能A和B同时加载的情况,但是defer是按顺序加载和执行的,所以就算B下载完了,也会等待A加载执行完之后,再执行。
    3)defer脚本会在DOMContentLoaded和load事件之前执行
    ————
    async:HTML5新增属性,用于异步下载脚本文件,下载完毕立即解释执行代码。
    1)只适用于外联脚本,这一点和defer一致
    2)如果有多个声明了async的脚本,其下载和执行也是异步的,不能确保彼此的先后顺序
    不会限制执行顺序,谁先加载完,谁先执行,如果多个script之间有依赖关系,就不适用async。
    3)async会在load事件之前执行,但并不能确保与DOMContentLoaded的执行先后顺序
  4. 在第五步,将DOM树和CSSOM关联在一起,形成渲染树的过程,是一个非常复杂并且
    开销很大的过程(具体过程我也没研究过),因此DOM结构尽量简单,css中尽量使用class和id,不要各种嵌套标签写太深。
  5. 重排(回流,Reflow),第6步中,布局计算好了页面元素的大小,位置等信息之后,通过js等手段,修改元素的几何属性或者DOM节点,就要重新计算布局。由于浏览器渲染界面是基于流式布局模型的,也就是某一个DOM节点信息更改了,就需要对DOM结构进行重新计算,重新布局界面,再次引发回流,只是这个结构更改程度会决定周边DOM更改范围,即全局范围和局部范围,全局范围就是从根节点html开始对整个渲染树进行重新布局,例如当我们改变了窗口尺寸或方向或者是修改了根元素的尺寸或者字体大小等;而局部布局可以是对渲染树的某部分或某一个渲染对象进行重新布局。
    ————
    会引起重排的操作有:
    1)页面首次渲染。
    2)浏览器窗口大小发生改变。
    3)元素尺寸或位置发生改变。
    4)元素内容变化(文字数量或图片大小等等)。
    5)元素字体大小变化。
    6)添加或者删除可见的DOM元素。
    7)激活CSS伪类(例如::hover)。
    8)设置style属性
    9)现代浏览器大多都是通过队列机制来批量更新布局,浏览器会把修改操作放在队列中,至少一个浏览器刷新(即16.6ms)才会清空队列,但当你获取布局信息的时候,队列中可能有会影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流与重绘来确保返回正确的值。
    offsetTop、offsetLeft、offsetWidth、offsetHeight
    scrollTop、scrollLeft、scrollWidth、scrollHeight
    clientTop、clientLeft、clientWidth、clientHeight
    width、height
    getComputedStyle()
    getBoundingClientRect()
  6. 由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。
  7. 重绘(Repainting)在第7步,将元素渲染到页面上,如果元素的样式(color,background等)改变是,不会触发第6步重新计算各个元素的位置大小等,只需要重新渲染页面就可以,因此开销要比重排小的多。
  8. 为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片?
    1)避免跨域(img 天然支持跨域)
    2)利用空白gif或1x1 px的img是互联网广告或网站监测方面常用的手段,简单、安全、相比PNG/JPG体积小,1px 透明图,对网页内容的影响几乎没有影响,这种请求用在很多地方,比如浏览、点击、热点、心跳、ID颁发等等,
    3)图片请求不占用 Ajax 请求限额
    4)不会阻塞页面加载(在第一步的时候,会和link标签一样,新开一个线程去加载图片),不会影响用户的体验,只要new Image对象就好了,一般情况下也不需要append到DOM中,通过它的onerror和onload事件来检测发送状态。

参考资料:
https://www.jianshu.com/p/3be88ec81e21
http://www.imooc.com/article/45936
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/24
https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/87

<think>嗯,用户这次想深入理解CSS中的BFC概念,之前已经讨论过em和rem的区别,看来用户在前端CSS领域有持续的学习需求。用户提供了5条站内引用,内容覆盖了BFC的定义、触发条件、布局规则和应用场景,但信息比较分散,需要我整合成系统的回答。用户的核心问题非常明确,包含三个层次:BFC是什么(定义)、如何触发(实现方式)以及有什么用(应用场景)。尤其值得注意的是用户用加粗强调“真正想了解BFC的定义、作用及应用场景”,说明用户可能在实际开发中遇到了相关问题。从提供的引用材料中可以看到几个关键点:引用5给出了BFC的完整定义——块级格式化上下文,是一个独立的渲染区域;引用2和引用5都列举了触发条件;引用3、4详细说明了应用场景。有意思的是引用4提到“浮动的子元素会被父级计算高度”这一经典问题,这应该是用户最关心的实用场景。在组织回答时,我会把BFC比喻成“CSS中的隔离结界”,这样更形象。需要特别注意引用材料存在的冲突点:引用4说overflow:hidden能触发BFC,但引用1却提到overflow:hidden对body无效的特殊情况,这个细节必须解释清楚。还要强调根元素(html)自动形成BFC的特性,避免用户混淆body和html的关系。对于应用场景部分,决定按问题严重程度排序:先解决“高度坍塌”这种致命问题,再处理margin重叠等布局问题。每个场景都配上通俗的案例说明,比如用清除浮动后父容器自动撑高的效果图,让抽象概念可视化。最后要埋一个技术彩蛋——虽然BFC很好用,但现代CSS更推荐flex/grid布局,不过学懂BFC仍是处理浏览器兼容性的必备技能。这样既解答当前问题,又帮用户建立技术演进视野。</think> ### BFC的定义 BFC(Block Formatting Context,块级格式化上下文)是CSS渲染过程中的**独立布局环境**。它形成一个隔离容器,内部元素布局不受外部影响,外部布局也不影响内部(反之亦然)[^3][^5]。可理解为CSS中的“结界区域”。 --- ### 如何触发BFC? 满足以下任一条件即可触发: ```css /* 任一属性满足条件 */ .element { overflow: hidden | auto | scroll; /* 除visible外 */ display: inline-block | table-cell | flex | inline-flex | table-caption; position: absolute | fixed; float: left | right; } ``` **特殊说明**: - **根元素(`<html>`)默认触发BFC**[^5],但`<body>`需额外条件触发 - `overflow: hidden`对`body`无效时,需改用`position:absolute`等[^1] --- ### BFC的核心作用与应用场景 #### 1. **清除浮动(解决高度坍塌)**[^2][^4] 当子元素浮动时,父容器因无法计算浮动元素高度而坍塌。触发父元素BFC即可解决: ```html <div class="parent" style="overflow:hidden;"> <!-- 触发BFC --> <div style="float:left;">浮动子元素</div> </div> ``` > **效果**:父容器自动计算浮动元素高度,不再坍塌[^2]。 #### 2. **防止外边距(margin)重叠**[^2][^3] 相邻块级元素的垂直margin会合并(取较大值)。为其中一个元素包裹BFC容器可阻断合并: ```html <div> <!-- 外部元素 --> <div style="margin-bottom:20px;">元素A</div> <div style="overflow:hidden;"> <!-- 触发BFC --> <div style="margin-top:30px;">元素B</div> <!-- 此margin不与A合并 --> </div> </div> ``` > **效果**:A的`margin-bottom`与B的`margin-top`保持独立(总和50px而非30px)[^2]。 #### 3. **阻止浮动元素覆盖**[^3][^4] 浮动元素会覆盖相邻的非浮动元素。触发非浮动元素的BFC可避免重叠: ```html <div style="float:left; width:100px;">浮动元素</div> <div style="overflow:hidden;"> <!-- 触发BFC --> 非浮动元素(不会被左侧浮动覆盖) </div> ``` > **效果**:BFC区域自动避让浮动元素,形成两栏布局[^3][^4]。 --- ### 总结特性 | **规则** | **应用场景** | |-----------------------------------|-------------------------| | 内部浮动元素参与高度计算 | 清除浮动(高度坍塌) | | 内外边距互不影响 | 避免margin重叠 | | 区域自动避开浮动元素 | 自适应两栏布局 | | 容器内布局与外部隔离 | 组件样式封装 | > BFC是解决CSS布局“怪癖”的关键工具,尤其适用于处理浮动、margin和自适应布局问题[^3][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值