浏览器渲染过程
html准备过程
收到字节数组->字符串->token(标签)->dom节点
css准备过程
- 生成cssom数很费性能,要匹配所有html标签,找出对应css样式,所以尽量用id和class,不要用继承等
收到字节数组->字符串->token(标签)->cssom节点
生成渲染树
- dom和cssom两棵树组合为渲染树
- 渲染树只会包括需要显示的节点和这些节点的样式信息,如果某个节点是 display:none 的,那么就不会在渲染树中显示。
- 如果没有加载js脚本,dom和cssom是并发执行,有了js加载,因为js会改变cssom,所以dom和cssom互相阻塞了
- 渲染过程遇到script,因为渲染和js引擎用的是一个线程(因为js会改变dom,所以在一个线程不会引发冲突)会阻塞,去执行解析执行js,如果这时候cssom没有加载完,需要等待cssom构建玩,然后再执行js,最后再构建dom
布局与绘制
布局
- 根据渲染树来布局(回流、重排),浏览器计算各个节点在页面中的确切位置和大小,输出盒模型,会精确地捕获每个元素在视口内的确切位置和尺寸,所有相对测量值都将转换为屏幕上的绝对像素。
绘制
- 布局完成,浏览器发出paint事件,绘制像素
回流与重绘
- 回流包括重绘
- 更改尺寸才会回流,重新计算大小
- 重绘只是重新渲染颜色等,不会重新计算大小,所以性能消耗要小
引起回流的操作
添加或者删除可见的DOM元素;
元素尺寸改变——边距、填充、边框、宽度和高度
内容变化,比如用户在input框中输入文字
浏览器窗口尺寸改变——resize事件发生时
计算 offsetWidth 和 offsetHeight 属性
设置 style 属性的值
减少回流与重绘
1.使用 transform 替代 top
2.使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)
3.不要把节点的属性值放在一个循环里当成循环里的变量。
for(let i = 0; i < 1000; i++) {
// 获取 offsetTop 会导致回流,因为需要去获取正确的值
console.log(document.querySelector('.test').style.offsetTop)
}
4.不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame
5.CSS 选择符从右往左匹配查找,避免节点层级过多
6.将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。
异步加载或执行js
- 异步加载,加载完成后马上执行,可能会阻塞DOMContentLoaded,一定会阻塞load
<scriptasyncsrc="script.js"></script>
- 姨妈加载,最后执行,不会阻塞
<scriptdefersrc="script.js"></script>
页面生命周期
1.HTML页面的生命周期有以下三个重要事件:
2.DOMContentLoaded — 浏览器已经完全加载了HTML,DOM树已经构建完毕,但是像是 <img> 和样式表等外部资源可能并没有下载完毕。
3.load — 浏览器已经加载了所有的资源(图像,样式表等)。
beforeunload/unload -- 当用户离开页面的时候触发。
每个事件都有特定的用途
1.DOMContentLoaded -- DOM加载完毕,所以js可以访问所有DOM节点,初始化界面。
2.load -- 附加资源已经加载完毕,可以在此事件触发时获得图像的大小(如果没有被在HTML/CSS中指定)
3.beforeunload/unload -- 用户正在离开页面:可以询问用户是否保存了更改以及是否确定要离开页面。
考点
1.cssom和dom构建是否会相互阻塞
2.渲染引擎和js引擎是否是一个线程
3.如果让js异步加载或执行
4.页面的生命周期
5.回流与重绘的区别
6.如何减少回流