渲染过程
按照时间顺序分为:构建DOM树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。
构建DOM树
浏览器无法解析HTML,将HTML解析为浏览器可以理解的DOM树
样式计算
- 浏览器无法理解CSS,转换为stylesheets。
- 转换样式表中的属性值,使其标准化(长度->px,颜色->rgb,变量->数值)。
- 计算DOM树中每个节点的具体样式。继承和层叠。
最终输出每个节点的ComputedStyle值。
布局过程
计算DOM树中***可见元素***的几何位置。
- 创建布局树。添加可见节点,忽略不可见节点。
- 布局计算。
分层
渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树。
并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。
满足以下条件
- 拥有层叠上下文属性的元素会被提升为单独的一层。https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context
- 需要剪裁的地方也会被创建为图层。
为每个图层生产绘制列表。
提交到合成线程。
分块
将图层分成图块。合成线程会将较大、较长的图层(一屏显示不完,大部分不在视口内)划分为图块(tile, 256256, 512512)。
光栅化
在光栅化线程池中,将视口附近的图块优先生成位图(栅格化执行该操作);
快速栅格化:GPU 加速,生成位图(GPU 进程)。
合成
-
绘制图块命令——DrawQuad,提交给浏览器进程;
-
浏览器进程的 viz 组件,根据DrawQuad命令,绘制在屏幕上。
重排、重绘、合成
- 更新了元素的几何属性(重排)
-
更新元素的绘制属性(重绘)
从图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
-
直接合成阶段
在上图中,我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。
JavaScript执行机制
1 变量提升
JavaScript代码执行过程中,需要先做变量提升,这是应为JavaScript代码在执行之前需要先编译
在编译阶段,变量和函数会存放到变量环境中,变量的默认值会被设置为undefined;在代码执行阶段,JavaScript引擎会从变量环境中查找自定义函数。
如果编译阶段,存在相同的函数,后定义的会覆盖掉之前定义的。
运行机制:先编译,后执行
2 调用栈
执行上下文:
- 当JavaScript执行全局代码的时候,会编译全局代码并创建全局执行上下文,在整个页面的生存周期内,全局执行上下文只有一份。
- 当调用一个函数三十,函数体内的代码会被编译,并创建函数执行上下文。一般情况下,执行完成后,创建的函数执行上下文会被销毁。
- 当使用eval函数时,eval的代码也会被编译,并创建执行上下文。
执行上下文包包括变量环境、词法环境。
变量环境:var function
词法环境:调用
调用栈就是用来管理函数调用关系的一种数据结构。
JavaScript中用来管理执行上下文的栈成为执行上下文栈,即调用栈。
函数调用时,入栈,执行完成出栈。
当一次有多个函数被调用时,通过调用栈(Call Stack)就能够追踪到哪个函数正在被执行以及个函数之间的调用关系。
3 块级作用域
作用域(scope):指程序定义变量的区域,该位置决定了变量的什么生命周期(变量和函数的可见性和生命周期)。
在ES6之前,只有全局作用域和函数作用域。
- 全局作用域中的对象在代码的任何地方都能访问到,其生命周期伴随着页面的生命周期
- 函数作用域就是在函数内部定义的变量或函数,并且定义的变量或函数只能在函数内部访问到。执行结束后,内部定义的的变量会被销毁