探究浏览器的渲染原理

68 阅读3分钟

1.网页的解析过程

网页的解析过程

2.浏览器的渲染流程

WebKit main flow.

渲染流程

1.从服务器获取到文件index.html 生成DOM Tree

2.遇到link标签后 下载CSS文件 生成CSSOM Tree(CSS对象模型)

3.根据DOM Tree和CSSOM Tree生成Render Tree

4.在渲染树上运行布局(Layout)以计算每个节点的几何体(布局是确定呈现树中元素的高度,大小,位置信息等等)

5.将每个节点都绘制到屏幕上

Render Tree

布局和绘制的步骤

注意:

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树

遇到script后文件发生堵塞

造成这种情况的原因

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:先下载完哪个文件加载哪个文件(完全独立)