浅析浏览器是如何工作的
一、渲染引擎
主流程:
HTML解析生成解析树 DOM树
首先将文档转换成另一种格式;
词法分析器:将输入内容分解成一个个有效标记。
HTML解析器:根据语言的上下文相关语法规则分析文档的结构,两两配对,构建解析树。
解析算法:边识别标记,边传递给树构造器,如此反复。
CSS解析生成样式表 CSSOM
理论上来说,应用样式表不会更改 DOM 树,因此似乎没有必要等待样式表并停止文档解析。但这涉及到一个问题,就是脚本在文档解析阶段会请求样式信息。如果当时还没有加载和解析样式,脚本就会获得错误的回复,这样显然会产生很多问题。这看上去是一个非典型案例,但事实上非常普遍。Firefox 在样式表加载和解析的过程中,会禁止所有脚本。而对于 WebKit 而言,仅当脚本尝试访问的样式属性可能受尚未加载的样式表影响时,它才会禁止该脚本。
JavaScript解析
解析器遇到
HTML5可将脚本标记为异步。
预解析,在执行脚本时,其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。
构建渲染树 layer tree
生成 DOM 树和 CSSOM 树以后,就需要将这两棵树组合为:渲染树。由可视化元素按照其显示顺序而组成的树。
呈现器是和 DOM 元素相对应的,但并非一一对应。display 属性值为“none”,那么也不会显示在呈现树中(但是 visibility 属性值为“hidden”的元素仍会显示)。
布局 layout tree
当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流)。这一阶段浏览器要做的事情是要弄清楚各个节点在页面中的确切位置和大小。通常这一行为也被称为“自动重排”。
绘制
布局完成后,浏览器会立即发出“Paint Setup”和“Paint”事件,将渲染树转换成屏幕上的像素。
总结:
通过网络请求或读取本地文件获取到html二进制流,从头开始逐行解析html文件生成 DOM树
;中间遇到css,异步解析生成 css规则树
;DOM树结合css规则树生成 layer tree
(display:none不会在此生成节点;::before、::after会生成节点;layer tree上的节点都会在页面渲染);通过layer tree生成 layout tree
(对应在屏幕上实际显示的位置);遍历layout tree,并调用呈现器的“paint”方法,将内容显示在屏幕上。
二、JS引擎(JS解释型语言,运行时类型检查)
JS引擎在执行时,会先初始化执行环境,包括:堆和栈空间、事件循环系统、垃圾回收等。
v8 5.9版本之前
解析器、 Full-codegen编译器、 Crankshaft编译器
js 由解析器
解析后,生成 AST 抽象语法树、作用域, 通过Full-codegen编译器( JIT即时编译器
)直接编译成 机器代码(内存中保留机器代码),直接执行机器代码;中间分析线程会收集优化信息,帮助Crankshaft编译器
来生成优化后的机器代码,然后需要优化的源码重新解析生成AST由Crankshaft使再生成优化后的机器代码。
弊端:放弃了在字节码阶段可以进行的一些性能优化;保存的机器代码,相较于中间字节码占用更多的内存空间;无法很好的支持和优化JS的新语法特性。
优势:减少了从中间字节码转换时间,直接执行机器代码,保证了执行速度。
v8 5.9版本
解析器、 lgnition解释器、 TurboFan编译器
js 由 解析器
解析生成 AST抽象语法树、作用域 ,由解释器lgnition
转换成 bytecode字节码(丢弃AST,内存中保留bytecode),生成的bytecode直接被解释器执行,之后每次执行都要由 解释器
解释成 机器代码 执行。经过 解释器
多次执行收集到的优化信息,把可优化代码由 编译器
编译成 优化后的机器代码,之后执行优化后的机器代码加快运行效率。
逆向还原:在某些情况下,优化后的机器码可能会被逆向还原成字节码,由解释器来解释执行。比如:参数类型突然变成另外一种类型,等。
优势:不需要一开始直接编译成机器码,而是生成了中间层的字节码,字节码的生成速度远远大于机器码的,所以网页初始化解析执行JS的时间缩短了,网页就可以更快的onload;内存中保留中间字节码,减轻机器代码占用的内存空间;