浅谈浏览器渲染

1 大致流程

在这里插入图片描述
浏览器端会把HTML解析成DOM树,CSS文件解析成CSSOM =》 两者解析完成后 DOM 和 CSSOM合并 =》 浏览器会进一步解析为Render Tree =》 浏览器计算每个元素在页面上位置和大小(Layout) =》 最后绘制到页面上 (Paint )

2 步骤详解

2.1 解析html

输入网址后,浏览器发起请求,首先得到的是html文档,当我们的浏览器获得html文件后,浏览器会自上而下的加载,通过HTML Parser解析器对html文档进行解析。
加载说的就是获取资源文件的过程,如果在加载的过程中,遇到外部css文件和图片,浏览器会另外发出一个请求,来获取css文件和相应的图片,这个请求是异步的,并不会影响html文件。
但是如果遇到javascript文件,html文件会挂起渲染的线程,等待javascript加载完毕后,html文件再继续渲染。
html代码中是如何生成DOM树的:
用如下代码演示生成DOM树的过程:

<html>
	<body>
		<h1>HelloWorld</h1>
		<div>
			<div>
				<p>picture:</p>
				<img src="example.png"/>
			</div>
      <div>
				<p>A paragraph of explanatory text...</p>
			</div>
		</div>
	</body>
</html>

首先树构建器接收到标签解析器发来的起始标签名后,会加入到栈中,图1是解析到<h1>标签的栈中压入的内容,共有<html><body><h1>三个标签,此时还未向DOM树中添加任何结点(图中黑色实线框代表开始标签,红色虚线框代表结束标签,结束标签不会入栈)。
在这里插入图片描述
继续向下解析,接收到一个结束标签,此时查询栈顶元素,如果和传入的结束标签属于同种类型的p标签(如图2),则将栈顶元素弹出,向DOM树中加入此节点,然后继续向下解析(如图3)。
如果遇到的是没有封闭标签的元素如,则直接加入DOM树中即可,无需入栈。
依次向下解析,当栈为空,即根节点也加入到DOM树中,DOM树构建完毕。
当某个元素缺失结束标签时,会怎么样?浏览器会自动补一个结束标签。

2.2 css解析

遇到内联的css样式就开始解析,外部的css文件,则在接收到了之后通过CSS Parse解析器进行解析。最终把css语句解析成为CSSOM(CSS Object Model)。
在解析css的同时也在解析dom,两者是并发的,所以等到css解析完毕就会逐步的渲染页面了。
当所有的css文件下载完且所有的CSSOM构建结束后,就会和DOM一起生成Render Tree。
所以,页面渲染依赖于css的加载。
为了形成Render Tree,浏览器大致做的事情有:
从DOM树根节点开始,遍历每一个可见的节点
一些节点是完全不可见的(比如 script标签,meta标签等),这些节点会被忽略,因为他们不会影响渲染的输出
一些节点是通过CSS样式隐藏了,这些节点同样被忽略——样式是display:none;
对每一个可见的节点,找到合适的匹配的CSSOM规则,并且应用样式
注意:“visibility:hidden”和“display:none”是不同的,“visibility:hidden”将元素设置为不可见,但是同样在布局上占领一定空间(例如,它会被渲染成为空盒子),但是“display:none”的元素不会挂载到render tree上,不是布局中的一部分 。
可参考下图对比其关系:
在这里插入图片描述

2.3 js解析

html加载过程中 ,遇到js就立即解析下载执行。

当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行,然后继续构建DOM。每次去执行JavaScript脚本都会严重地阻塞DOM树的构建,如果JavaScript脚本还操作了CSSOM,而正好这个CSSOM还没有下载和构建,浏览器甚至会延迟脚本执行和构建DOM,直至完成其CSSOM的下载和构建。
为什么html需要等待javascript呢?因为javascript可能会修改DOM,导致后续的html资源白白加载,所以html必须等待javascript文件加载完毕后,再继续渲染。这也就是为什么javascript文件要写在底部body标签前的原因。

所以,script 标签的位置很重要。实际使用时,可以遵循下面两个原则:
CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。
JS置后:我们通常把JS代码放到页面底部,且JavaScript 应尽量少影响 DOM 的构建。

如果是外部的,还可以在<script>标签上加上defer或async属性

  • defer可以让js的下载不影响html的后续解析,且在html解析完了再执行js文件,且按照原来的下载顺序
  • async也是让js的下载不影响html的后续解析,但一旦下载完了就立即执行,因此也无法保证按照下载顺序执行

css解析和js解析时互斥的,css阻塞js的执行,但是不会阻塞外部脚本的加载。
js逻辑对于dom节点的依赖关系,因为js可能会影响到render tree,为了避免浪费浏览器资源,执行js代码时,dom和css解析都会被冻结,js执行完了,才能执行后续的内容。

但是,当JS执行时间过长会页面阻塞,那么JS就真的对CPU密集型计算无能为力么?
所以,后来HTML5中支持了 Web Worker

通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。

2.4 回流

概念:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)
当页面布局、元素几何属性发生变化时 就会发生回流,此时我们需要重新验证并计算渲染树。所以如何回流过于频繁,也就会增加浏览器的工作量,降低了浏览器性能。
触发页面重布局的属性

  • 盒子模型相关属性会触发重布局
  • 定位属性及浮动也会触发重布局
  • 改变节点内部文字结构也会触发重布局
    在这里插入图片描述

2.5 重绘

概念:当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
只触发重绘的属性
在这里插入图片描述
关系:当重绘时不一定会发生回流,但是回流时一定是需要对页面进行重绘。

3 性能优化

从性能优化的角度,减少浏览器的计算,尽量减少浏览器的重排和重绘,尤其是重排。

  1. 避免使用通配规则。如 *{} 计算次数惊人!只对需要用到的元素进行选择。
  2. 避免设置大量的style属性,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow,所以最好是使用class属性。
  3. 实现元素的动画,它的position属性,最好是设为absoulte或fixed,这样不会影响其他元素的布局。
  4. 动画实现的速度的选择。比如实现一个动画,以1个像素为单位移动这样最平滑,但是reflow就会过于频繁,大量消耗CPU资源,如果以3个像素为单位移动则会好很多。
  5. 不要使用table布局,因为table中某个元素旦触发了reflow,那么整个table的元素都会触发reflow。那么在不得已使用table的场合,可以设置table-layout:auto;或者是table-layout:fixed这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围。
  6. 如果CSS里面有计算表达式,每次都会重新计算一遍,触发一次reflow

部分摘自:
HTML文档解析和DOM树的构建
借助performance工具直观理解浏览器的渲染过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值