参考
浏览器渲染进程通过网络进程获取数据
浏览器网络进程
处理和服务器建立了TCP连接后,向服务器发送http请求;服务器响应给浏览器网络进程
HTML文件。- 浏览器通过服务器的响应头
Content-Type=text/html
判断这是一个HTML文档。浏览器就可以将二进制格式转化为可读的HTML文件。将HTML元素转换为JS对象,对象创建完成,会根据元素的嵌套结构形成DOM树; 浏览器网络进程
将请求回来的数据转发给浏览器主进程
,浏览器主进程
发消息给渲染进程
,渲染进程
和网络进程
建立管道,将数据传输给渲染进程Chrome V8引擎
就工作在渲染进程中
浏览器渲染引擎对HTML文本进行解析和渲染
- 渲染模块比较复杂,所以分为多个子模块进行处理
- 每个子模块都有输入内容,处理过程,输入内容
- 所以输入HTML经过浏览器渲染模块处理后形成屏幕上的像素,就是渲染流水线
总结流程渲染流水线:
HTML文档交给渲染引擎后,渲染引擎将HTML解析为浏览器可识别的DOM树,将DOM树的每个节点附上计算好的CSS属性值,计算带有CSS的DOM节点的位置和大小生成布局树。
创建DOM树
- 为什么要构建DOM树,因为浏览器无法直接使用HTML,所以需要将HTML转化为浏览器能够理解的DOM树。
- HTML解析器将超文本和标签解析为DOM树
在开发者工作,Console控制台,输入document
:document的结构就是DOM结构和HTML结构一致,和HTML不同的是DOM是保存在内存中的树状结构,我们可以直接使用JS来获取和修改。
通过JS修改DOM内容
document.querySelector('.title-content-title').innerHTML='nihao zhongguo';
细节:
- DOM就是浏览器自己的语言,每个节点对象相连形成父子关系,后续浏览器对某个节点进行操作就会比较方便了
- 遇到link标签就会向服务器发送请求,请求外部链接,如css文件
- 遇到script标签,就会向服务器发送请求,请求js文件
- 主线程会先暂停解析HTML文件,先执行JS文件中的JS代码,JS代码执行完成之后才会继续解析HTML文件
- img标签、css文件、defer,不会影响主线程,而是会异步的加载
- 先返回js文件或者css文件看具体情况,如果先返回js文件,解析js文件的时候发生阻塞,不能先执行js文件,必须等CSSOM构建完成,才能执行js文件,因为渲染树是需要DOM和CSSOM构建完成以后才能构建,JS是可以控制CSS样式的,所以这一步
- 浏览器有一个预扫描线程,会提起那扫描html文件,会提前把css,js和字体文件下载下来,下载是异步的,不影响主线程
输入&输出
- 输入:HTML
- 输出:DOM树
样式计算: 根据CSS属性值计算每个DOM节点的样式
HTML加载CSS的三种方式:
- 通过
link
引入CSS文件 <style>
标签内的CSS- 元素的
style属性
内嵌的CSS
分为三步:
1.将CSS解析成浏览器能够理解的结构:styleSheets
Chrome控制台通过document.styleSheets
可以查看到,目前没看到内联样式。
2. 将属性值标准化
p {color:red}
red
是浏览器无法理解的,需要将它转化为浏览器可以理解的标准化的计算值
- 将red转化为rgba数值
- 将em转化为px
3.计算DOM树中每个节点的具体样式
计算方法: 根据DOM的继承关系来计算样式
- 样式来源的文件
- user agent浏览器默认样式
- 继承样式
- 每个样式属性也会根据w3c设置一个默认值 CSS层叠样式
- CSS样式来源于多个源的属性值的算法,这是CSS最大的特点,CSS的全程就是
层叠样式表
输入&输出
- 输入DOM节点
- 这个阶段输出每个DOM节点的样式,并保存在
computedStyle
结构中。
布局阶段
计算DOM树中可见节点的位置和大小
- 创建布局树
- 只包含可见的节点
- 浏览器根据节点样式计算每个可见节点也就是没有设置display:none的节点位置和宽高,计算好了就可以在屏幕上绘制了
- 布局计算
- 计算布局树节点的位置。计算完重新写入布局树
输入&输出
- 输入:布局树
- 输出:布局树 没有清晰的分离输入输出,这是Chrome需要优化的点
这样每个节点都拥有了样式和位置大小
生成图层树
页面的展示就是多个图层按照一定的顺序叠加在一起展示的。
查看开发者工具的Layers
浏览器会为哪些元素创建涂层?
- 拥有层叠样式的CSS属性
- 需要裁剪的地方
- overflow
- 滚动条会生成一层涂层
- 创建涂层
- 渲染引擎绘制涂层
查看图层绘制的过程
渲染引擎如何绘制涂层
涂层的绘制是由渲染进程的合成线程完成的。
- 图层的绘制列表准备好了后
- 主线程会把该绘制列表提交(commit)给合成线程,那么合成线程是如何工作的
图层可能很大,但是用户只会看到视口部分,如果绘制全部涂层开销会很大,所以合成线程会将图层划分为图块。
- 合成线程会按照视口附近的图块优生成位图。生成位图的操作是由栅格化来执行的。栅格化就是将图块转化为位图,会结合GPU进程完成 -合成与显示:
- 合成线程生成绘制图块DrawQuad命令,给浏览器主进程
- 主进程viz组件接收合成线程DrawQuad命令,将页面内容绘制到内存中,最后再将内存显示在屏幕上
渲染流水线总结
- 渲染进程将HTML内容转化为可读懂的DOM 树
- 渲染引擎将CSS样式表转化为可读懂的styleSheets,计算出DOM树节点的样式
- 创建布局树,根据盒子模型计算出DOM树节点的布局信息,存放在computed
- 对布局树进行分层,创建图层树
- 为每个图层生成绘制列表,并将其提交给渲染进程的合成线程
- 合成线程将图层分成图块,并在栅栏化线程池中将图块转化为位图
- 合成线程发送绘制图块命令DrawQuad给浏览器进程
- 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上
重排、重绘、合成
- 更新元素的布局信息:重排
- 主进程:DOM-styleSheets-Layler-Layler-Paint-合成
更新元素的布局信息:如果使用JavaScript或CSS修改了元素的属性,
div.style.height
重排需要更新渲染流水线重新来一遍。开销最大。
- 主进程:DOM-styleSheets-Layler-Layler-Paint-合成
更新元素的布局信息:如果使用JavaScript或CSS修改了元素的属性,
- 更新元素的绘制属性:重绘
- 主进程:DOM-styleSheets-Layler(跳过)-Layler(跳过)-Paint-图层合成(非主线程) 使用JavaScript修改元素的背景颜色等绘制属性,布局阶段不会重新执行,开销比重排小
- 修改了一个既不要布局也不要绘制的属性:合成
- 主进程:DOM-styleSheets-Layler(跳过)-Layler(跳过)-Paint(跳过)-图层合成(非主线程) 使用transform:tranlate实现动画效果,会跳过布局和绘制过程,直接进入图层合成,而且是在非主线程上合成的,没有占用主线程资源,所以开销最小,能够提升绘制效率。
defer属性的script标签
上面步骤完成后,设置了defer的script标签开始加载并执行
<script defer src={...} />
页面加载和渲染完成,用户可以进行页面交互