1.网页的解析过程
2.浏览器的渲染流程
渲染流程
1.从服务器获取到文件index.html 生成DOM Tree
2.遇到link标签后 下载CSS文件 生成CSSOM Tree(CSS对象模型)
3.根据DOM Tree和CSSOM Tree生成Render Tree
4.在渲染树上运行布局(Layout)以计算每个节点的几何体(布局是确定呈现树中元素的高度,大小,位置信息等等)
5.将每个节点都绘制到屏幕上
注意:
1.link标签不会阻碍DOM Tree的构建过程 但是有可能会阻碍Render Tree的构建过程
2.DOM Tree和Render Tre不是一一对应的关系 (display:none)
3.回流和重绘解析
1.回流(reflow 重排)
对节点的大小、位置修改重新计算称之为回流
如何能引成回流
1.DOM结构发生改变(添加/移除节点)
2.改变了布局(修改width、height、font-size)
3.窗口resize(修改窗口的大小)
4.调用getComputeStyle方法获取尺寸、位置信息
🌰
<div id="display">显示的内容</div>
<button id="btn">点击隐藏</button>
<script>
document.getElementById('btn').addEventListener('click', () => {
document.getElementById('display').remove();
});
</script>
2.重绘(paint)
重新渲染页面称为重绘
如何能引成重绘
1.修改背景色、文字颜色、边框颜色、样式等
注意
回流一定会引起重绘 回流是一件很消耗性能的事情
如何避免回流
1.修改样式尽量一次性修改
2.避免频繁操作DOM
3.避免通过getComputeStyle获取尺寸、位置信息
4.对某些元素使用position:absolute/fixed(开销相对较少 不会对其他元素造成影响)
4.合成和性能优化(以内存管理作为代价)
1.composite合成
- 绘制的过程中 可以将布局后元素绘制到多个合成图层中
- 默认情况下 标准流的内容都是被绘制在同一个图层(layer)中的
- 一些特殊的属性 会创建一个新的合成层 并且新的图层可以利用GPU来加速绘制(每个合成层都是单独渲染的)
哪些属性可以形成新的合成层?
1.3D transforms
2.video、canvas、iframe
3.opacity 动画转换时
4.position:fixed
5.will-change:(一个实验性的属性 提前告诉浏览器元素可能发生哪些变化)
6.animation或transition设置了opacity、transform
5.defer和async属性
前置知识
当页面上遇到script标签时 不能继续构建DOM树 —> 下载JS文件并执行JS脚本 —> JS脚本执行完毕 继续解析html文件 构建DOM树
造成这种情况的原因
1.JS的作用之一就是操作DOM元素 并修改DOM
2.等DOM构建完成并且渲染后再下载JS文件 会造成严重的回流和重绘 造成性能损失(影响页面的性能)
3.遇到JS文件优先下载JS代码 再构建DOM树
新的问题
1.脚本比html重 处理时间更长
2.首屏加载缓慢 页面解析堵塞
解决问题的方法
1.defer
defer 属性告诉浏览器不要等待脚本下载 而继续解析HTML 构建DOM Tree
脚本会由浏览器来进行下载 但是不会阻塞DOM Tree的构建过程
如果脚本提前下载好了 它会等待DOM Tree构建完成 在DOMContentLoaded事件之前先执行defer中的代码
🌰
HTML
<div id="app">app</div>
<div id="title">title</div>
<script defer src="defer.js"></script>
<script defer src="defer1.js"></script>
<script>
// 后执行 因为defer会在DOMContentLoaded之前被执行
window.addEventListener('DOMContentLoaded',()=>{
console.log('DOMContentLoaded');
})
</script>
<h1>测试</h1>
JS
defer.js
console.log('defer log');
const title = document.getElementById('title');
console.log(title)
defer1.js
console.log('defer1 log');
执行结果
// defer log
// <div id="title">title</div>
// console.log('defer1 log');
// DOMContentLoaded
注意:
1.defer仅适用于外部脚本 对于script默认内容会被忽略
2.defer可以提高页面的性能 并且推荐放到head元素中
3.多个带defer的脚本是可以保持正确的顺序执行的
2.async
async是让一个脚本完全独立的
1.浏览器不会因 async 脚本而阻塞(与 defer 类似)
2.async脚本不能保证顺序 它是独立下载 独立运行 不会等待其他脚本
3.async不会能保证在DOMContentLoaded之前或者之后执行
🌰
<script async src="async.js"></script>
共同点
都是异步加载JS文件
不同点
1.defer:按照顺序加载文件
2.async:先下载完哪个文件加载哪个文件(完全独立)