Appearance
解析流程
参考
当浏览器从上到下解析整个HTML文档时,
如果遇见外部URL资源,就会发送请求加载对应文件(现代浏览器可能会同时发送多个请求加载文件)。
如果遇见内联的样式表,就会立即解析(但不一定会立即渲染出样式);如果遇见内联的脚本,就会立即解析和执行;
样式表阻塞
当HTML解析器遇见一个style标签时,会按顺序解析里面的样式;当HTML解析器遇见一个link标签,会发送一个外部样式表的请求。这样的后果是到导致后面的后面的JS代码
- 如果是内联脚本,则必须等待前面的样式表加载和解析完成才会执行
- 如果是外部脚本,浏览器会发送下载外部脚本文件的请求(CSS文件和JS文件可能同步下载),即使文件已经返回,也必须等待前面的样式表加载和解析完成
这是因为JS执行依赖最新的CSS渲染(或者说最精确的样式信息)。
脚本阻塞
如果遇见普通的script(无async或defer)时,不论他是内联脚本还是外部脚本,都会阻塞浏览器进一步解析HTML文档(即暂时无法处理这个脚本后面其他需要加载的URL),而必须等待该标签代表的脚本文件执行完毕(如果是外联的脚本,甚至需要等到这个js文件加载成功并执行完毕。
这是因为:JS可能影响后续的文档,可能向文档流中插入信息,也可能改变后续script脚本的全局变量,因此浏览器干脆在解析和执行script标签的时候,阻塞后续文档的解析
整理上面可以得到下面结论
- CSS文档的加载和解析,阻塞的是脚本的执行而不是脚本的加载。
- 同步JS脚本的加载解析和执行,是会影响HTML解析器的工作,导致后面的所有资源都无法被加载。
DOMContentLoaded与load
当文档中没有脚本时,浏览器解析完文档便能触发 DOMContentLoaded 事件;如果文档中包含脚本,则脚本会阻塞文档的解析,而脚本需要等位于脚本前面的css加载完才能执行。在任何情况下,DOMContentLoaded 的触发不需要等待图片等其他资源加载完成。
页面上所有的资源(图片,音频,视频等)被加载以后才会触发load事件,简单来说,页面的load事件会在DOMContentLoaded被触发之后才触发。
参考
script
标签上的async
和defer
的区别,参考Script - MDN
script标签上的async
和defer
属性,决定了脚本加载时采用同步方式还是异步方式。
- 如果不加上这两个属性,默认为同步加载脚本,加载和执行时会阻塞页面的渲染,即浏览器按顺序解析DOM树及脚本,遇见脚本会阻塞DOM树生成并执行脚本。
- async 和 defer 方式是用异步的方式加载脚本,不会阻塞页面渲染,它们之间的不同在于何时执,
- async 方式是加载后马上执行,
- defer 方式是加载后等所有 DOM 都渲染好触发 DOMContentLoaded 事件之前执行,
- 所以 async 方式里面的脚本都是乱序执行,defer 方式加载的代码都是按序执行的,按序执行对有依赖的代码非常重要。
- 若两个属性同在,会忽略defer而遵从async
通过动态生成script的方法,可以实现JSONP
等功能
渲染流程
参考:
下面展示了大概的渲染过程
- 浏览器首先解析三个东西:
- HTML/SVG/XHTML,产生一个DOM Tree。
- CSS,产生CSS Rule Tree。
- Javascript,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.
- 解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意:
- Rendering Tree 渲染树并不等同于DOM树,因为一些像Head或display:none的东西就没必要放在渲染树中了。
- CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个DOM结点。
- 然后计算DOM结点的位置,这又叫layout和reflow过程。
- 最后通过调用操作系统Native GUI的API绘制。
Repaint重绘
Repaint表示页面上某个元素的的非定位样式需要重新绘制(比如利用JS改变了元素节点的背景颜色,此时不需要重新布局)
Reflow回流
Reflow表示元素节点的几何尺寸发生了变化,此时需要重新计算并构建渲染树(即此时需要重新布局),成本比Repaint的成本高得多的多
下面是几条减少Repaint和Reflow的原则
- 不要一条一条地修改DOM的样式,如果修改的样式过多可以将样式统一在某个类中,然后直接更改元素节点的className;
- 使用临时变量保存DOM节点,而不是每次都直接对DOM节点进行操作(减少元素节点的读写),在JS性能与浏览器性能方面都能得到一些优化;
- 尽可能修改层级比较低的DOM,缩小操作的影响范围;
- 放弃使用table进行布局,因为一个很小的改动都会造成整个table的重新布局
在每一帧中完成的工作
参考:看了就会的浏览器帧原理
可以看见requestAnimationFrame
是在帧开始的时机执行的,而requestIdleCallback
是在帧末尾的空闲时间执行的
使用Chrome调试工具查看渲染流程
参考