浏览器渲染原理
1. 浏览器的渲染过程
对浏览器渲染过程通过一张流程图来展示这五个关键步骤:解析HTML构建DOM树、解析CSS生成CSSOM规则树、构建渲染树、布局以及绘制。
解析 HTML 构建 DOM 树 解析 CSS 合并DOM与CSSOM构造渲染树 布局处理 绘制阶段
在这个过程中:
- 解析HTML:浏览器首先接收到来自网络的HTML文档,并开始解析它,将其转换为一系列的DOM节点,形成DOM树。
- 构建CSSOM规则树:同时,浏览器会解析CSS文件,创建CSSOM(CSS对象模型),它也是一个树状结构,包含了所有CSS规则。
- 合并DOM与CSSOM构造渲染树:DOM树和CSSOM树结合,生成渲染树。这个树只包含需要渲染到屏幕上的元素及其计算后的样式信息。
- 布局处理(回流):接下来,浏览器会计算渲染树中每个节点的精确位置和尺寸,这个过程称为布局或回流。
- 绘制阶段(paint):最后,浏览器遍历渲染树,调用渲染对象的paint方法,将各个节点绘制到屏幕上,完成页面的视觉呈现。
这个流程确保了网页内容能够正确、高效地展示给用户。
注意:为了极致提升用户体验,网页渲染引擎采取了一种智能化的分步加载与展示机制。它并不会等到HTML文档全部接收完毕才启动渲染流程,而是采取即时解析、即时显示的策略。这意味着,一旦接收到哪怕是部分HTML代码,渲染引擎就会立刻开始解析这部分内容,同时并行地构建和布局渲染树,让页面元素随着数据的到来逐步展现给用户。
此过程被称为渐进式渲染或增量渲染,在这一过程中,用户几乎能实时看见页面逐步成形,而非等待所有内容加载完毕。即便剩余的HTML仍在通过网络传输中,页面顶部或已下载部分内容已可先行显示。这样的设计极大缩短了用户感知到的等待时间,提高了交互的实时性。下面👇是对其一些关键的优化。
2. 浏览器渲染优化
在进行浏览器渲染优化时,核心目标是提升网页加载速度和用户界面响应效率。以下是基于您提供内容的优化描述总结:
JavaScript 优化
- 脚本放置:将
<script>
标签移至</body>
前,确保DOM构建和渲染先行,减少JavaScript对页面初次渲染的阻塞。 - 异步加载:利用 async 和 defer 属性实现JavaScript的非阻塞性加载。async
适用于互不影响的脚本,允许并行加载但不保证执行顺序;defer 确保脚本按顺序执行,且都在DOM解析完成后执行,两者都能提升页面加载速度。
CSS 优化
- 优选 link:推荐使用 方式引入CSS,因为它不会中断HTML解析,相较于 @import 能更快地呈现视觉内容。
- 内联CSS:对于关键的初始样式,可考虑内联以减少HTTP请求,加速首次渲染。
- CSS位置:将CSS放在 中,确保在渲染前获取到样式信息,避免无样式内容的闪烁(FOUC)。
DOM与CSSOM优化
- 简化结构:保持HTML结构扁平化,减少DOM深度,提高解析效率。
- 语义化标签:使用语义化标签提升页面结构清晰度,减少渲染引擎的解析负担。
- 优化CSS选择器:简化CSS选择器,减少复杂度,提高CSSOM构建速度。
回流与重绘管理
- 最小化DOM操作:通过操作DOM树的底层节点、减少对样式的直接操作来减少昂贵的回流和重绘事件。
- 布局与定位技巧:利用 position: absolute/fixed 脱离文档流,避免不必要的布局变化。
- 批量处理:集中执行DOM操作和样式修改,利用浏览器的渲染队列机制,减少渲染周期次数。
流式解析(Streaming Parsing):
渲染引擎并不等待HTML文档全部到达就开始解析工作,而是接收到多少数据就解析多少,即时构建DOM树。这意味着用户可以在文档还未完全下载时就开始看到页面内容。
提前渲染(Early Painting):
一旦渲染引擎构建了初始的DOM树,并与已下载的CSSOM合并生成渲染树,它就会立即进行布局和绘制操作,即使此时HTML文档还在继续下载。这样,用户可以迅速看到页面顶部的内容,即便页面底部的资源尚未加载。
懒加载(Lazy Loading):
对于图片和视频等非关键资源,可以采用懒加载技术,即在页面滚动到可视区域时才开始加载。这样可以减少初始页面加载时间,提高首屏渲染速度。
其他策略
资源缓存与压缩:实施HTTP缓存策略,压缩CSS/JS文件,减少加载时间。
3. 渲染过程中如何处理 JS 文件?
在现代Web开发中,处理JavaScript文件的加载、解析与执行以优化渲染过程,确实是一个关键点,特别是在追求快速首屏渲染和提升用户体验方面。以下是几种处理方式的详细说明:
-
将
<script>
标签置于</body>
结束标签之前这是传统做法,目的是确保在执行JavaScript之前,DOM已经完全构建完成。这样可以避免JavaScript因试图访问尚未加载的DOM元素而导致的错误,并且在一定程度上减少了JavaScript对页面初次渲染的阻塞。
-
使用defer属性
对于那些不需要立即执行、并且执行顺序重要的脚本,可以在
<script>
标签中使用defer属性。defer告诉浏览器该脚本可以在文档解析完成后,DOMContentLoaded事件触发之前执行,而且会按照脚本在文档中出现的顺序执行。这样既保证了JavaScript的非阻塞加载,也维护了脚本的执行顺序。 -
使用async属性
对于那些不依赖于页面DOM,且相互之间也没有依赖关系的外部脚本,可以使用async属性。async属性使得脚本异步加载,并且一旦加载完成就会立即执行,不会按照脚本在文档中的顺序执行。这对于性能统计脚本、广告脚本等独立功能非常适用,因为它不会阻塞页面渲染,但可能在页面完全加载前或之后的任何时刻执行。
-
模块化和代码拆分
采用模块化开发,通过工具(如Webpack)进行代码拆分,按需加载JavaScript模块。这样,只有在特定功能或路由需要时才会加载相应的JS文件,减少了首屏加载的负担。
-
懒加载JavaScript
类似于图片的懒加载,可以对非首屏必要的JavaScript进行懒加载,即在页面滚动到特定位置或在特定条件满足时才开始加载和执行脚本。
-
预加载和预读取
使用或可以提示浏览器提前加载关键的JavaScript文件,尽管这些资源可能稍后才会用到。preload用于关键资源,确保它们优先加载;prefetch则用于可能在将来需要的资源,比如下一页的资源,这有助于减少后续页面的加载时间。
4.文档预解析
文档的预解析(Pre-parser或Pre-parsing)是一种现代浏览器采用的优化技术,旨在进一步加速网页加载速度。这项技术特别针对的是在浏览器执行JavaScript脚本期间,如何高效利用资源和时间,以减少页面加载的整体耗时。
当浏览器遇到JavaScript脚本时,默认情况下,会暂停HTML的解析过程,等待脚本下载并执行完成,这是因为JavaScript有可能会修改当前的DOM结构或影响后续资源的加载(比如动态插入新的HTML、CSS或JavaScript)。这种机制虽然确保了执行顺序的准确性,但也可能导致页面渲染的延迟。
预解析技术就是在这一背景下诞生的优化策略。具体来说,当浏览器的主线程正在忙于执行某个脚本时,会启动一个额外的后台线程(在支持该功能的浏览器如WebKit和Firefox中)来预先解析文档中剩余未被处理的部分。这个预解析线程不会改变现有的DOM树,也不会执行脚本,它的主要职责是提前发现和排队后续需要加载的资源,比如识别出外部脚本、样式表和图片等资源的引用。
通过预解析,浏览器能够提前了解到哪些资源将是下一步需要加载的,并可以尽早开始这些资源的下载,即使它们实际被加入DOM或执行还需等待当前脚本完成。这样一来,当主线程完成脚本执行并准备继续解析HTML时,许多后续资源可能已经加载完毕或至少已经开始加载,从而显著减少了页面加载的总时间,提高了页面渲染的效率和用户体验。
需要注意的是,预解析是浏览器内部的优化策略,开发者无需直接操作或干预这一过程,但了解这一机制有助于更好地设计和优化网页资源加载策略,比如合理安排脚本的加载和执行顺序,以及利用defer和async属性来指导浏览器更高效地处理资源。
5.CSS 如何阻塞文档解析?
在CSS与文档解析的关系中,尽管CSS不直接参与DOM树的构建,但它对JavaScript执行和后续的渲染过程有着重要影响,从而间接影响了文档的解析流程。具体来说,CSS阻塞文档解析的情况主要体现在以下几个方面:
-
JavaScript 需要样式信息
当浏览器遇到一个脚本(非异步加载,即不使用async或defer属性的脚本),并且这个脚本在执行过程中可能需要查询某些元素的样式信息时,浏览器会等待CSSOM(CSS对象模型)构建完成。这是因为,如果在CSSOM构建完成之前执行脚本,脚本可能会基于不完整的样式信息进行计算或操作,导致显示错误或不符合预期的行为。因此,为了确保JavaScript能够获取到正确的样式信息,浏览器会暂停文档的解析和JavaScript的执行,直到相关CSS文件加载并解析完毕。
-
阻塞渲染
即使脚本本身并不查询样式信息,CSS文件的加载仍然可能阻塞页面的首次渲染。因为浏览器希望在首次渲染时就能呈现出页面的最终外观,而不是先显示无样式的页面内容,随后再进行样式重绘。为了达到这一目的,浏览器通常会等待所有关键CSS资源加载完成,构建CSSOM,再与DOM结合生成渲染树,之后才进行布局和绘制。这意味着,即使JavaScript脚本不直接依赖于CSS,CSS的加载也会影响到页面内容首次可见的时间。
-
CSS 加载优先级
浏览器通常会优先下载与渲染路径直接相关的CSS资源,这进一步体现了CSS加载对文档解析和渲染流程的影响。由于样式对页面视觉呈现至关重要,浏览器会给予CSS文件较高的优先级,有时甚至会优先于图片等其他资源的加载,确保页面能够尽快以正确的样式显示给用户。
尽管CSS不直接改变DOM树,但它通过影响JavaScript执行和页面首次正确渲染的需求,间接导致了文档解析的阻塞。因此,在优化页面加载速度时,合理安排CSS文件的加载顺序,使用媒体查询、link标签的media属性以及CSS的异步加载技术(如使用rel="preload"预加载CSS但不阻塞渲染),都是减轻这种阻塞效应的有效策略。
6. 优化关键渲染路径
优化关键渲染路径是提升网页加载速度和用户初次体验的关键环节。为实现这一目标,聚焦于缩小三个核心变量至关重要:
- 减少关键资源数量:每一项关键资源都会增加浏览器解析页面的工作负担,因此,应仔细审查并剔除非必要的CSS、JavaScript文件和图片,或将它们安排在不影响首屏渲染的时间点加载。
- 缩短关键路径长度:通过分析资源间的依赖关系,确保没有不必要的阻塞情况发生。利用async或defer属性加载脚本,使资源下载与解析并行不悖,从而加快页面构造进程。
- 压缩关键资源的字节大小:运用各种压缩工具和技术来减小CSS、JavaScript文件及图像的体积,比如使用GZIP压缩,将图片转为更高效的WebP格式,以及实施代码简化策略。
具体实施步骤如下:
合理安排资源加载 使用工具分析 移除非首屏资源 压缩资源 优化加载策略 完成
- 分析诊断:首先,利用性能分析工具(例如Lighthouse、PageSpeed
Insights)来识别当前关键路径上的资源总量、总字节大小和加载时间,形成基准线。 - 资源精简:基于诊断结果,着手移除或延迟非首屏渲染所必需的资源加载。对于可延后的内容,采用懒加载技术。
- 字节优化:对所有关键资源执行压缩和优化操作,确保它们在网络上传输时占用的带宽最少。这包括代码压缩、图片优化和资源格式现代化。
- 加载策略调整:合理规划资源的加载顺序,利用预加载(preload)指令确保关键资源优先获取,同时采用正确的脚本执行策略减少渲染阻塞。
7. 什么情况会发生阻塞渲染,如何避免?
- HTML解析 :
初始HTML文档的解析是渲染的前提,因此在浏览器没有完成对HTML结构的基本理解前,渲染过程无法开始。这包括等待外部HTML片段(如通过<link rel="import">
引入的)加载和解析。 - CSS解析与构造渲染树 : 浏览器遇到CSS时(无论是内联样式、
<style>
标签还是外部CSS文件),会暂停渲染流程来构建CSSOM(CSS对象模型)。直到CSSOM构建完成并与DOM结合生成渲染树,才能计算布局和绘制像素。因此,未加载或解析CSS会阻塞渲染。 - Script标签默认行为 :无特殊属性的
<script>
标签会阻塞DOM构建及渲染。浏览器会暂停解析HTML来执行JavaScript,以防脚本操作尚未加载的DOM部分。这意味着位于首屏渲染关键路径上的JS会显著延迟页面首次内容渲染的时间(FCR)。 - 资源加载 :
大型图片、字体文件或其它重型资源如果被首屏内容依赖,也会阻塞渲染,直到它们至少部分加载完成。这尤其是当使用<img>
标签且没有指定宽高或使用display:none隐藏图片时,会导致浏览器重新布局。 - Script标签的defer与async属性 :
defer :允许脚本并行下载,但保证所有带有defer属性的脚本在HTML解析完成后,DOMContentLoaded事件触发之前按顺序执行。这不会阻塞DOM构建,但可能影响首次可交互时间(TTI)。
async: 脚本异步下载且不会阻塞DOM解析,下载完成后立即执行,不保证执行顺序。适合无依赖的脚本,减少渲染阻塞。
优化渲染的关键在于管理好资源的加载顺序与执行时机,确保关键渲染路径上的资源尽快加载并执行,同时利用现代浏览器提供的特性(如defer和async)来最小化JavaScript对渲染流程的影响。