浏览器的组成

从输入网址之后,浏览器会调用自己的核心功能,他会去调用网络模块,得到网页之后,就调用到渲染引擎,渲染引擎就会开始呈现页面,如果遇到了 JS,会先用网络模块下载 JS,然后让 JS 解释器执行这个 JS。
对于前端来必须关注 渲染引擎,JS解释器,网络模块 的基本原理

HTML 的解析过程

1
2
3
4
5
// index.html
<link -> CSS>
<h1>helll world</h1>
<script -> JS>
<div>more...</div>

对应的解析过程

1
2
3
4
5
6
|下载 HTML
|--|解析 HTML -- 构建 DOM 树,并不是直接渲染
|--|--|下载 CSS
|--|--|--|解析 CSS -- 构建 CSS 树
|--|--|--|--|下载JS -- 当这里遇到 JS 的时候会中断 HTML的解析
|--|--|--|--|--|执行 JS -- 直至 JS 执行完毕

这里有个疑问,是完全下载完以后再解析还是边下载边解析?
答案是不知道,这个没有文档规定,每个浏览器都可以按自己的理解去实现。

HTML 和 JS

为什么 JS 的下载和执行,会阻塞 HTML?
原因是 JS 的执行可能会修改到 DOM 树,所以必须等待 JS 执行完,但是为什么下载也会阻塞呢?因为过去的浏览器“偷懒了”,有没那么强大,只会在看到这行 script 的时候,才会去下载,所以一旦开始下载就必须等待他解析完毕。>但是这个事情可以优化的,他可以去下载过程提前,拿到文档之前,先扫描一遍,有 CSS、JS就提前开始下载,但在早期为什么不这么实现呢?因为比如上述四行代码,这个执行过程是很快的,例如只有 1ms,再怎么提前开始下载也就只节约了这 1ms,而下载时间可能有 100ms,即使做了这个优化,也只是把 100ms 优化成了 99ms,意义不大,所以早期浏览器就没有做。

async 与 defer 的区别

  1. 普通的 script,就是下载和执行都会阻塞 HTML 解析。
  2. async 就是遇到 script 时,就开始下载,什么时候下载完就什么时候开始执行,仿佛脱离了主分支,使结构更加松散。
  3. 而 defer 也是解析到 script 时,就开始下载,但是会等待 HTML 解析完毕之后、DOM ready 之前,开始执行。

这里的 module 几乎和 defer 差不多,只不过多了一些分支下载,执行依旧在 HTML 解析完毕以后

HTML 和 CSS

什么影响,可以同时进行,HTML可以边解析,边下载和构建 CSS。

CSS 和 JS

CSS的下载和解析会阻塞 JS 的执行

因为 JS 需要读取 CSS 的结果,假设上述代码的第一行 <link -> CSS> 有 100M,文件的最后一行写了 h1 {height: 999px;}
当下面在执行 JS 的时候获取 h1 的高度应该是多少?
很容易就能推出是 999px,所以肯定是等 CSS 下载和执行完毕才会去执行 JS。

页面渲染原理:布局、绘制、合成

DOM 树

HTML结构

生成的 DOM 树

CSS 树

CSS 代码

生成的 CSS 树

CSS 树和 DOM 树并无关联(DOM 树不知道样式,CSS 树不知道DOM结构),即使 HTML 结构中没有 h1、h2 标签,但是 CSS 树中依然会构建出 h1、h2,所以必须合成一个渲染树

render 树

渲染树 CSSOM

拿到 render tree 以后终于可以开始渲染了,但是并不能直接开始渲染,所以下一步就是布局。

布局

布局就是计算元素的大小和尺寸,接着就是绘制

绘制

布局以后就知道要对那些元素(主要是当前屏幕内)进行着色和阴影这些个东西。

合成

最后一步就是合成,因为有些元素可能会重叠,比如说两个 div 重叠在一块,在计算颜色的时候,就要拍平、压扁,如:上面的 div 是红色的,下面是绿色的,拍扁以后,红色就会盖住绿色。
主要是把多层次的东西,合成一个层次,最终就可以输出在屏幕上,也就是用户看到的了。

如何判定 reflow 和 repaint

比如页面有上下两个 div,当用 JS 把上面的 div 高度增加时,就要重新去布局,重新布局就是 reflow,浏览器要重新计算位置,大小尺寸变化,一般来说大小变化,颜色也会发生改变,也就需要重新绘制,重新绘制就是 repaint,最后就是重新合成了(因为所有的改变都会触发重新合成,所以一般不讨论)。
有些属性会造成 reflow,有些属性会造成 repaint,一般来说需要一个一个实验才知道,但是可以直接用别人的 成果
根据上述流程上图,一般来说造成了重新布局(reflow)就一定会触发重新绘制(repaint)