【计算机原理交集】一起探讨和梳理下,浏览器怎么解析HTML文件的

文章目录

  • 前言
  • dom的解析
    • 影响dom解析的资源
      • script标签
    • 不会影响dom解析的资源
      • 图片
      • link标签
  • css的解析
  • 布局layout
  • 绘制paint
  • 主线程交接合成器线程
  • 重排(回流)和重绘
    • js见缝插针的执行
    • 不走重排和重绘
      • 可控的地方
      • 不可控制的地方
  • dom树解析碰到script标签会先布局和绘制/还是边解析边渲染?
  • css会阻塞dom的解析吗
  • window.onload和DOMContentLoaded的区别
  • 怎么修改link标签和script标签对页面加载的影响

前言

以下内容纯属个人的理解,如果有错误,或者讲的比较含糊的地方,欢迎大佬指正。

理解浏览器怎么解析HTML文件的,我认为有利于我们去分析一些性能问题。

ok,咱们来看看浏览器拿到HTML文件后做了什么。


dom的解析

拿到html文件后,首先UI线程会创建一个渲染器进程(内核),然后浏览器通过IPC通道把html数据传输给渲染器进程,接着渲染器进程的主线程把HTML字符串从上到下解析成DOM树

HTML字符串 ---> 经过tokeniser标记化,通过词法分析解析成多个标记 --->
识别标记进行DOM树构造,创建Document对象,然后向下发展成完整DOM树。

大白话就是根据节点的层级关系,从第一行开始,深度优先遍历节点,将其所有节点解析成一个dom树的结构,这个dom树的节点已经是js对象了。

但是此时的dom树节点上并没有样式信息。

影响dom解析的资源

在dom解析的过程中,会有一些影响dom解析的因素存在,主要都是一些资源。

script标签

例如在dom中间插入script标签

<body>
    <div>
        1
    div>
    <script>
        debugger
    script>
    <div>
        2
    div>
body>

你会发现,1显示在页面上了,但是此时执行了debugger代码,然后2没有显示了。这是因为阻塞了后面dom的解析。

1能正常显示出来,是不是能说明dom的解析和真正把样式渲染上去是即时的呢?还有就是有办法优化script标签阻塞问题吗?这些后面了解了整个过程后会讲。

不会影响dom解析的资源

例如图片,css文件,不会阻碍dom解析。

图片

这个很好理解,你日常就会遇到过。例如图片很大的时候,页面已经渲染完成,但是图片一点一点的加载出来。

link标签

当head里的link链接的css资源容量很大,导致下载时间很漫长,是不会阻塞dom的解析。

但是有个误区要注意下,不阻塞dom的解析不意味着页面能正常渲染。因为页面的渲染也要等待css的解析完成才能开始,所以从最终的结果来看,是会阻塞页面的渲染。

我自己实验了一个demo,我把第一个css的link文件弄的很大,然后网络选慢速,结果页面会先白屏很久等待link文件的下载。

当然有优化的手段,后面会讲


css的解析

浏览器遍历 CSS 中的每个规则集,根据 CSS 选择器创建具有父、子和兄弟关系的对象节点树。这玩意和dom树类似的,我们叫CSSOM树

听说这个css构建到CSSOM树的速度特别快,性能不会有太大的损耗(MDN上说的)。那么我就在想了,之前网上说的那些选择器的性能问题是不是可以忽略了。


布局layout

通过对DOM树和CSSOM树的遍历结合,生成的带有样式的树,被称为layout树或者render树

这个树有一些需要注意的地方:

  1. 一些不会挂在layout树的标签,比如scriptmetalink等。
  2. render树上没有属性为display:none的节点。但visibilityopacity隐藏的节点,还是会显示在渲染树上的,因为他们占位了。
  3. 伪元素不会显示在DOM树,但会显示在渲染树上。

所以是不是可以证实浏览器元素审查工具上看的并不是layout树?

通过遍历layout树上每个节点的信息,确定好节点的位置,占宽(其实样式我认为也得到了,但这一步最重要的应该是确立每个节点的位置、占宽)的过程应该就叫做布局


绘制paint

通过遍历layout树,把节点的层级关系确立成绘制记录表,这个过程叫做绘制

其实这个概念有歧义,有的把内容渲染到页面上的过程也囊括在内了,例如MDN。但这里我就以不包括为主了。

所谓的层级关系就是z-index,而绘制记录表不止记录了层级关系,还有所有的样式信息。


主线程交接合成器线程

以上的这些事情都是主线程做的,说明主线程不仅要执行js代码,还要解析dom、做布局、做绘制。真是个卷王。

当然也会有他实在搞不过来的事情,例如接下来他要把layout树和绘制记录表给合成器线程,后者拿到后进行图层分离和栅格化。

图层分离,就是把节点安装图层关系绘制出来,例如咱们用ps软件作图,每一个图片都是用图层堆叠起来的。

那栅格化是个啥玩意?我看网上讲的好复杂,我大概理解为合成器线程把图层分离后的内容给栅格线程,栅格线程处理成一维图层,返回给合成器线程。

然后合成器线程根据拿到的东西做成一帧的内容,给GPU渲染到网页上。


重排(回流)和重绘

如果我们此时改了节点的高度,位置,就会影响到其他节点的布局,这个时候就要重新进行布局layout以及接下来该走的流程,这就叫做重排或者回流

如果我们只是改了个颜色、背景啥的,是不会影响到其他节点布局的,那么只需要从绘制paint开始重新走,这就是重绘

为啥大家说尽量不要引起重排和重绘呢?因为主线程有可能在你重排重绘的时候需要执行,这个时候就会挤占浏览器的重排和重绘。

例如页面里有个方块在做往复运动,说明主线程一直在走重排、重绘、合成器线程渲染的步骤。这时候一个js程序执行了,而且还在不断的执行,此时重排、重绘的时间必定被js的执行挤占。导致重排、重绘、合成器线程渲染的步骤变慢了,那是不是每一帧的生成延迟了。

也就是说我原来1s可以渲染出60张图片的,结果因为js的执行,我只能输出30张。页面看起来是不是就卡顿了。

改善的方法也是有的!

js见缝插针的执行

如果要保证刷新率为60帧,那就是一帧的生成时间要为17ms内,也就是重排、重绘、合成器线程渲染的步骤要在17ms内做完。

然而每一帧的生成时间会出现可能只需要用12ms、9ms等情况,因为重拍重绘只是在主线程做,且合成器线程和栅格线程做事快滴很。

那么剩下的几ms是不是可以偷偷给js去用呢?这就是js的见缝插针。

有个叫requestAnimationFrame的api就是专门干这个事情的,具体使用可以看这里

不走重排和重绘

可控的地方

,和有transform、opacity、filterscss属性的盒子,自己独立实例化一个图层,只在合成器线程和栅格线程执行,不会占用主线程,所以不会触发重排和重绘。

这样主线程js再怎么执行也不会影响到页面渲染,不过这样虽然可以提高性能,但是它以内存管理为代价,因此不应作为 web 性能优化策略的一部分过度使用(MDN上说的,不是我瞎编的哈哈)。

此外还有一些方式也可以减少重排绘制:

  • 避免多次随意修改样式,可以一次同时修改
  • 利用BFC
  • 使用createDocumentFragment批量操作DOM
  • css放在中,不要异步加载css文件
  • img标签提前定义宽高,例如在做图片懒加载的时候

不可控制的地方

当然一些行为会让我们没有办法保证不走重排的过程,例如窗口大小的调整也会影响页面的布局,不得已引发重排重绘。


dom树解析碰到script标签会先布局和绘制/还是边解析边渲染?

前面提到的script标签阻塞dom解析的那个例子,我们可以看到1先展示在页面了。

是不是说明了dom解析到script标签后,会先把script标签以上的dom解析、布局、绘制、合成显示在页面上。

不过网上也有人说是dom解析、布局、绘制、合成显示这个过程是渐进式的,也就是一步一步即时运行的。

两种说法我暂时以第一种为主吧。


css会阻塞dom的解析吗

前面也提到说css的解析不会影响dom的解析,可并行,也就是说拿到css文件后能够和dom树一起解析。

但是到了layout那一步还是需要css的解析结果的,所以到头来还是会影响到渲染。

css解析很快,所以一般来说都是下载时间影响了页面渲染时间,要警惕link过大的问题。


window.onload和DOMContentLoaded的区别

我觉得就按照我写的那样理解即可(链接)。我看网上说DOMContentLoaded是dom加载完就触发,样式表,脚本等其他资源不管。我觉得不太正确,例如:

<body>
    <div>1div>
    <script>
        window.addEventListener('load', function () {
            console.log('load');
        }) // 页面的全部资源加载完毕才会执行,包括图片、视频等
        document.addEventListener('DOMContentLoaded', function () {
            console.log('DOMContentLoaded');
        }) // dom渲染完毕即可,此时图片、视频还可能没加载完。
    script>
    <p>2p>
    <script>
        debugger
    script>
body>

这种情况,DOMContentLoaded都没打印出来。


怎么修改link标签和script标签对页面加载的影响

具体可以看【前端面试专栏】<script> 脚本以及 <link> 标签对 DOM 解析渲染的影响,讲的还是挺清楚的。

你可能感兴趣的:(计算机原理交集,html,前端,设计模式,javascript)