Skip to main content

浏览器解析 HTML 文件的过程

详细过程分析#

1)构建DOM树#

将HTML转换为浏览器能够理解的结构一-DOM 树

①HTML词法、语法的解析

  • 词法分析:把字符流初步解析成我们可理解的"词",学名叫token。
  • 语法分析:把开始结束标签配对、属性赋值好、父子关系连接好、构成dom树。

②请求js,css

2) 样式计算(Recalculate Style)#

  • 把CSS转换为浏览器能够理解的结构-styleSheets;
  • 转换样式表中的属性值,使其标准化;
  • 计算出DOM树中每个节点的具体样式
    • CSS继承,包含父节点的样式;
    • 样式层叠的规则: !important 无限高>内联1000> id 100> class 10> tag >继承>浏览器默认

3) 布局阶段#

  • 创建布局树L ayout tree, 构建一棵只包含 可见元素布局树; head标签, 不可见元素都不包含;
  • 布局计算

4)分层#

页面中有复杂效果,如3D变换、页面滚动,或者使用z-indexing 做Z轴排序等,渲染引擎还需要为特定的节点生成专用的图层,并生成棵对应的图层树 (L ayerTree)。

图层树和布局树的关系,每一 个节点自己没有单独的图层,就属于父节点的图层,反正所有的节点都会直接或者间接地从属于一个层,

哪些节点会创建新的图层?

  • 拥有层叠上下文属性的元素会被提升为单独的一层。比如:明确定位属性position: fixed、 定义透明属性opacity、使用CSS滤镜iter的元素、z-index
  • 需要剪裁(clip) 的地方也会被创建为图层。比如div内容的文字超出div范围,会被裁剪,渲染引擎单独为文字部分单独创建-一个层; 如果出现滚动条,滚动条也会被提升为单独的层;

5) 图层绘制#

  • 图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表 ;
  • 可以在开发者工具的L ayers点击document的profiler查看到绘制列表;

6)合成线程进行分块#

  • 主线程会把该绘制列表提交(commit) 给合成线程,进行真正的绘制;
  • 合成线程会将图层划分为图块(tile) ,这些图块的大小通常是256x256 或者512x512

7)栅格化#

  • 栅格化,是指将图块转换为位图。图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的;
  • 按照视口附近的图块来优先生成位图;
  • 栅格化过程都会使用GPU来加速生成,使用GPU生成位图的过程叫快速栅格化,或者GPU栅格化,生成的位图被保存在GPU内存中;

8)合成和显示#

  • 一旦所有图块都被光栅化,合成线程就会生成个绘制图块的命令- "DrawQuad",然后将该命令提交给浏览器进程 。
  • 浏览器进程里面有一个叫viz的组件,用来接收合成线程发过来的DrawQuad 命令,然后根据DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

通俗理解#

  1. 解析html: 通过字节流把内容传到html解析引擎, 解析引擎首先会调用解析器处理标签,处理没闭合或者使用不规范的标签,处理完毕后交给分词器处理。分词器将标签转换为token,并交给dom构建器处理。在解析过程中遇到script会暂停解析,直到script加载完成并执行完,html才会继续解析。
  2. 构建dom树。将token 解析为dom节点, 并将dom节点添加到dom树中。通过压栈出栈方式构建dom树。这个过程就是逐行解析代码,包括html标签 和js动态生成的标签,最终生成dom树。
  3. css树。 在解析的过程中,遇到css 资源需要加载时,htrml解析不会暂停。当css资源下载好,会调用css解析引擎去解析CSS代码,计算出最终的样式数据。对CSS代码中非法的语法她会直接忽略掉。解析CSS的时候会按照如下顺序来定义优先级:浏览器默认设置,用户设置,外链样式,内联样式,html中的style。 在解析css的同时,渲染树构建被暂停,直到css树解 析完成。
  4. 渲染树。把之前构建好的css树以及dom树按照-定的规则以及依赖关系生成渲染树,写到内存,等待进一 步生成界面。渲染树和dom树是有区别的。dom树完全和html标签一-对应, 但是渲染树会忽略掉不需要渲染的元素,比如head、 display:none的元素 等。
  5. 布局。从渲染树的根节点开始遍历,然后确定每个节点对象在页面上的确切大小与位置,布局阶段的输出是一个盒子模型,它会精确地捕获 每个元素在屏幕内的确切位置与大小。同时把每个节点做成一个纹理,放在gpu寄存器中管理
  6. 绘制。遍历渲染树,调用渲染器的paint()方法在屏幕上显示。