同系列文章请查看:Github:pre-interview
浏览器原理
概览:浏览器的解析渲染过程
1)解析 HTML 构建DOM树
2)解析 CSS 构建CSSOM树
3)利用上面两个树构建渲染树(渲染树的节点即为“渲染对象”)
4)渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以被称作“回流”),这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
5)上述几步过后,布局结束;最后进行绘制,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。
webkit的主要流程如图:
细节补充
- 首先解析收到的文档,根据文档定义构建一棵 DOM 树,DOM 树是由 DOM 元素及属性节点组成的。
- 然后对 CSS 进行解析,生成 CSSOM 规则树。
- 根据 DOM 树和 CSSOM 规则树构建渲染树。渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色和大小等属性的矩形,渲染对象和 DOM 元素相对应,但这种对应关系不是一对一的,不可见的 DOM 元素不会被插入渲染树。还有一些 DOM元素对应几个可见对象,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述。
- 当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
- 布局阶段结束后是绘制阶段,遍历渲染树并调用渲染对象的 paint 方法将它们的内容显示在屏幕上,绘制使用 UI 基础组件。
解析文档(HTML)过程中,是将HTML都解析完了再去生成渲染树吗?
注意:这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html 都解析完成之后再去构建和布局 render 树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
script中的脚本文件、link中的css文件的解析/执行会阻塞文档解析吗?如何阻塞?
- 脚本的加载会阻塞文档解析
如果没有 defer 或 async 属性,浏览器会立即加载并执行相应的脚本。它不会等待后续加载的文档元素,读取到就会开始加载和执行,这样就阻塞了后续文档的加载。
下图可以直观的看出三者之间的区别:
蓝色代表 js 脚本网络加载时间,红色代表 js 脚本执行时间,绿色代表 html 解析
所以script要放在底部/加async defer关键字
拓展学习:
JavaScript 的加载、解析与执行会阻塞文档的解析,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JavaScript,那么它会暂停文档的解析,将控制权移交给 JavaScript 引擎,等 JavaScript 引擎运行完毕,浏览器再从中断的地方恢复继续解析文档。
也就是说,如果想要首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。
当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。
- CSS 如何阻塞文档解析?
理论上,既然样式表不改变 DOM 树,也就没有必要停下文档的解析等待它们。
CSS的解析并不阻塞文档~
但是可以拓展一下,JS的脚本会因为CSSOM树没被构建好而延迟执行,所以!要把script放在底部 / 加async defer关键字
然而,存在一个问题,JavaScript 脚本执行时可能在文档的解析过程中请求样式信息(比如根据样式获取元素节点),如果样式还没有加载和解析,脚本将得到错误的值,显然这将会导致很多问题。所以——
如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此时运行脚本,那么浏览器将延迟 JavaScript 脚本执行和文档的解析,直至其完成 CSSOM 的下载和构建。也就是说,在这种情况下,浏览器会先下载和构建 CSSOM,然后再执行 JavaScript,最后再继续文档的解析。这样就会间接阻塞了文档解析。
什么情况会阻止浏览器渲染?
要明确——首先渲染的前提是生成渲染树
1)所以 HTML 和 CSS 肯定会阻塞渲染。
如果你想渲染的越快,你越应该降低一开始需要渲染的文件大小,并且扁平层级,优化选择器。
2)浏览器在解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方重新开始。
也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。
拓展知识
并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性
当 script 标签加上 defer 属性以后,表示该 JS 文件会并行下载,但是会放到 HTML 解析完成后顺序执行,所以对于这种情况你可以把 script 标签放在任意位置。
同理,对于没有任何依赖的 JS 文件可以加上 async 属性,表示 JS 文件下载和解析不会阻塞渲染(async属性不能保证JS文件的执行是按顺序的)。
记住这张图:
Tips: Please indicate the source and original author when reprinting or quoting this article.