Hello大家好,我是小蓝,又到了继续和大家分享技术内容的时间,今天的主题是"浏览器的渲染原理" 觉得有用的小伙伴们记得给小蓝一个赞哟~~
网页被解析的过程
一个网页url从输入到展示到浏览器中,要经历怎么样的解析过程呢
要想深入理解浏览器的渲染原理,我们得明白是浏览器的那个部分把网页渲染上去的。当一个网页被下载下来后,是由我们的渲染引擎来帮助我们解析的,也就是我们常说的浏览器内核
那渲染引擎在拿到一个html页面后,如何解析并最终呈现出我们的网页呢?
实际上这幅图只是简化后的渲染过程,更详细的渲染过程如下图 从上图
我们可以知道,实际上浏览器渲染过程分为HTML解析,css规则生成,Render Tree构建,布局和绘制,这几个过程最后再呈现出用户可以看到的网页。
HTML解析过程
因为默认情况下服务器会给浏览器返回index.html文件,所以解析HTML是所有步骤的开始;
解析HTML时会构建DOM Tree:
也就是说会把根据上图的代码创建下图的树形结构
单纯的DOM Tree还不够,还只是最原始最粗糙的网页,所以浏览器渲染的第二步就是生成css规则
CSS规则生成
在解析的过程中,如果遇到css的link元素,那么会由浏览器负责下载对应的CSS文件(注意一点:下载CSS文件是不会影响DOM的解析的)。
浏览器下载完CSS文件后,就会对CSS文件进行解析,解析出对应的规则树;我们可以称之为 CSSOM (CSS Object Model,CSS对象模型)。
用图片展示该过程如下:
当有了DOM Tree和 CSSOM Tree后,就可以两个结合来构建Render Tree了,有了Render Tree我们才能去布局和绘制页面
构建渲染树
在构建Render Tree的过程中,有两个注意事项,一是**link
元素不会阻塞DOMTree的构建过程,但是会阻塞Render Tree的构建过程,这是因为 RenderTree在构建时,需要对应的 CSSOM Tree**。第二点是Render Tree和DOM Tree并不是一一对应的关系 ,比如对于的元素,压根不会出现在Render Tree中。
有了Render Tree 以后我们就可以进行布局和绘制页面了display:none
布局(layout)和绘制(paint)
渲染的第四步是在渲染树(Render Tree )上运行布局(Layout)以计算每个节点的几何体。 渲染树 会表示显示哪些节点以及其他样式,但是不表示每个节点的尺寸、位置等信息布局是确定呈现树中所有节点的宽度、高度和位置信息
;渲染的第五步是将每个节点绘制(Paint)到屏幕上 在绘制阶段,浏览器将布局阶段计算的每个转为屏幕上实际的像素点frame
包括将元素的可见部分进行绘制,比如文本、颜色、边框、阴影、替换元素(比如)img
但是实际的web应用或者说我们的网站并不是一成不变的,往往会在用户点击或者滚动滚动条的时候导致页面部分或者整个页面发生变化,这个时候就有两个面试常问的概念:回流和重绘
回流和重绘
回流reflow:(也可以称之为重排) 在浏览器第一次确定节点的大小和位置时我们称之为布局(layout);之后对节点的大小、位置修改重新计算称之为回流。那么什么情况下会引起回流呢?
- 比如结构发生改变(添加新的节点或者移除节点)
DOM
- 比如改变了布局(修改了等值)
width、height、padding、font-size
- 比如窗口(修改了窗口的尺寸等)
resize
- 比如调用方法获取尺寸、位置信息;
getComputedStyle
理解了回流的概念,我们再看看重绘
重绘重绘: 浏览器第一次渲染内容我们称之为绘制(),如果后续重新渲染我们称为重绘。那么什么情况下会导致重绘呢?比如修改背景色、文字颜色、边框颜色、样式等均会引发重绘paint
注意
回流一定会引起重绘,所以回流是一件很消耗性能的事情。
那么在实际开发中,我们要尽可能的避免发生回流
- 修改样式时尽量一次性修改
比如通过cssText修改,比如通过添加class修改 - 尽量避免频繁的操作DOM 我们可以在一个DocumentFragment(文档碎片)或者父元素中 将要操作的DOM
操作完成,再一次性的操作; - 尽量避免通过getComputedStyle获取信息;
- 对某些元素使用position的absolute或者fixed
并不是不会引起回流,而是开销相对较小,不会对 其他元素造成影响。
特殊解析-composite合成
在浏览器渲染的第五步里面我们可以看到最后还有一个,那么他是什么意思呢?
实际上这是浏览器的一种优化手段,在绘制的过程中,可以将布局后的元素绘制到多个合成图层中。默认情况下,标准流中的内容都是被绘制在同一个图层(Layer)中的,而一些特殊的属性,会创建一个新的合成层( CompositingLayer ),并且新的图层可以利用GPU来加速绘制,因为每个合成层都是单独渲染的。
比如我们看京东的首页就有很多合成层
那么哪些属性可以形成新的合成层
呢?composite
- 3D 变换
- 视频、画布、iframe
- 不透明 动画转换时
- 位置:固定
- will-change:一个实验性的属性,提前告诉浏览器元素可能发生哪些变化;
- 动画或 过渡 设置了不透明度、变换;
要注意的是:分层确实可以提高性能,但是它以内存管理为代价,因此不应作为 web 性能优化策略的一部分过度使用。
script元素和页面解析的关系
我们现在已经知道了页面的渲染过程,但是JavaScript在哪里呢?事实上,浏览器在解析HTML的过程中,遇到了script元素是不能继续构建DOM树的;它会停止继续构建,首先下载JavaScript代码,并且执行JavaScript的脚本;只有等到JavaScript脚本执行结束后,才会继续解析HTML,构建DOM树。那么为什么浏览器要这样做呢?这是因为JavaScript的作用之一就是操作DOM,并且可以修改DOM;如果我们等到DOM树构建完成并且渲染再执行JavaScript,会造成严重的回流和重绘,影响页面的性能; 所以会在遇到script元素时,优先下载和执行JavaScript代码,再继续构建DOM树。
但是上述处理方式也往往会带来新的问题,特别是现代页面开发中。比如我们的vue、react,脚本往往比HTML页面更"重",处理时间需要更长;那么就会造成页面的解析阻塞,在脚本下载、执行完成之前,用户在界面上什么都看不到;也就是我们常说的白屏。
为了解决上述问题,元素给我们开发者提供了两个属性:和scriptdeferasync
推迟 defer 属性告诉浏览器不要等待脚本下载,而继续解析HTML,构建DOM Tree。也就是说脚本会由浏览器来进行下载,但是不会阻塞DOM Tree的构建过程;如果脚本提前下载好了,它会等待DOM Tree构建完成,在DOMContentLoaded事件之前先执行defer中的代码。所以DOMContentLoaded总是会等待defer中的代码先执行完成,而且多个带defer的脚本是可以保证正确的顺序执行的。从某种角度来说,defer可以提高页面性能,并且推荐放到head元素中。但是要注意defer仅仅适用于外部脚本。异步async 特性与 defer 有些类似,它也能够让脚本不阻塞页面。但他会让脚本完全独立,什么意思呢?
- 浏览器不会因 async 脚本而阻塞(与 defer 类似),
- 其次async脚本不能保证顺序,它是独立下载、独立运行,不会等待其他脚本;
- 最后async无法保证在DOMContentLoaded之前或者之后执行
defer
通常用于需要在文档解析后操作DOM的JavaScript代码,并且对多个文件有顺序要求的;而async通常用于独立的脚本,对其他脚本,甚至DOM 没有依赖的。script
今天的技术分享就到这里,想要了解更多前端方面的技术或前端学习资料,可以进入我们主页获取联系方式哦,或者在评论区留言&私信我