浏览器如何渲染页面
浏览器如何渲染页面
浏览器渲染页面的过程就像是盖房子,一般先请求服务器得到 HTML 文件,HTML 文件就相当于网页的框架结构,不过一开始浏览器得到的是显示字节内容的 HTML 文件,必须要内化为自己看的懂的语言才行,于是就把字节转化为字符,也就是程序员能够看的懂的 html 代码。但是此时我们可以看得懂,机器却不行,于是我们需要转化为机器能够看得明白的语言,HTML 是标记语言,里面有很多小于号 <和大于号> 分别标识不同的结构,于是浏览器就把字符转化为 Token,可以简单把 Token 理解为符号标签,比如遇见的第一个的时候转化为一个起始标签,遇到结尾的</ body > 时转化为结束标签,这样字符就被 Token 化了,不同字符就变成有不同特殊意义的东西了。Token 化 以后就要开始升华,也就是把 token 转化为节点对象。在编程里面,对象虽然只是用代码表示而已,但是对象是可以进行编程操作的,因为对象会有自己的属性方法,相当于把 token 盘活了。最后就是把这些节点对象都连到一起,形成文本对象模型,也就是 DOM。DOM 其实就是浏览器自己的语言,每个节点对象相连,形成父子关系,这样后面要对某个节点对象进行操作,对于浏览器来讲就非常友好了。
CSSOM
从 HTML 转化为 DOM,就像建房时把施工合同转化为迷你框架模型,在阅读施工合同时,其实也同时阅读了甲方的装饰要求,在制作迷你框架结构模型的同时我也正在给模型做 “装修”,比如给墙面上色之类的。一般来说我们会把 css 样式作为外链加入到 link 标签里面,css 就相当于网页的 “装饰”。浏览器在构架 DOM 的时候,就会遇到 link 标签,然后向服务器发送请求,接着得到 css 文件,后面的流程就和处理 HTML 文件非常相似。把字节转换成字符,字符转换为 token,Token 转化为节点。最后就是不同的地方了,这里节点会结合为 css 对象模型,也就是 CSSOM
渲染树
迷你模型再加上装修,也就是房子的模型完成了,有了模型,房子就更容易搭建了。虽然 DOM 和 CSSOM 都是独立的对象模型,但是一个网页的呈现离不开两者,因为一个网页就是由框架和样式结合起来的,DOM 和 CSSOM 就和起来就是渲染树。页面就是要根据渲染树的结构样式来进行的,但是 DOM 和 CSSOM 并不是简单的结合就完事了。因为渲染树上的节点是页面能够呈现的内容,也就是一些 HTML 标签和某些样式是不会被呈现出来的,不会被呈现出来的就不挂在渲染树上了,比如 meta 和 link 标签就不会当作内容呈现出来,设置了 display:none 的样式也不会被呈现出来。渲染树的任务就是匹配 DOM 和 CSSOM 的节点,并且捕获可见内容。
布局
虽然房子模型已经做好了,但不代表马上就能建房子了,实际上还需要安排房子的布局。实际上房子上需要多少块砖头,需要多少吨水泥等等。在渲染树构造完成以后,页面也是不能被马上渲染的,因为还需要进行布局。布局的意思就是获取渲染树的结构,节点位置和大小,而布局是依据盒子模型来进行的,也就是每个元素都用一个盒子来表示,然后这些盒子在页面上进行排列和嵌套。
绘制
在安排好房子的布局以后,就要开始建房子了。浏览器在布局以后其实就可以安排页面的绘制了,把渲染树以像素的形式绘制在页面上,这样我们的页面就呈现出来了。
渲染 HTML+CSS+JS
了解了渲染的基本过程以后,结合实际深入理解。来看一下以下的 html 代码
<html>
<head>
<link rel="stylesheet" type="text/css" href="index.css">
<script src="index.js"></script>
</head>
<body>
</body>
</html>
首先第一步:浏览器发送请求以后,服务器或者本地返回给浏览器 HTML 文件,这个时候第一步就是解析 HTML 文件,并且构建 DOM 树。在解析 HTML 文件的时候遇见 了 link 标签,浏览器就去请求 css 文件,请求 css 文件的同时也继续解析 HTML 文件。此时遇到了 script 标签,浏览器就去请求 JS 文件。服务器或者本地就会陆续返回 CSS 和 JS 文件,实际操作中会先得到 css 还是 JS 文件是要看及具体情况的。在这里,如果先返回并且解析完成 JS 文件也是会发生阻塞的,我们不能先执行 JS 文件,必须要等到 CSSOM 构建完成了才能执行 JS 文件。因为前面说过了渲染树是需要 DOM 和 CSSOM 构建完成以后才能构建,而且 JS 是可以操控 CSS 样式的,所以这一步就是解析 CSS 文件并且构建 CSSOM。如果是 script 标签里面写的是行内 JS 代码,而不是外部 JS 代码,也会发生阻塞吗?还是会。CSSOM 的构建就是渲染中一个重要的阻塞因素,其实 DOM 也是会阻塞渲染过程的,毕竟没有 DOM,页面的框架都建造不起来。但是 DOM 有一点好处,就是可以部分解析,而 CSSOM 不能部分解析,这就是 DOM 和 CSSOM 不同之处。那么为什么 DOM 可以部分解析,但是 CSSOM 却不可以呢?比如我们给 body 设置了字体为 32px,然后又给 body 里面的 div 设置了字体为 16px,如果 CSSOM 只解析了 body,后面的 div 没有解析或者延迟解析,那就乱套了,所以 CSSOM 不能部分解析。不过在解析 CSS 文件并且构建 CSSOM 的时候,浏览器依旧去下载并且解析 JS 文件,等 CSSOM 构建完成以后就可以执行 JS 里的内容,但是 HTML 的解析刚刚就已经停止了,因为 JS 会阻塞 HTML 解析。虽然看起来 JS 并没有直接阻塞渲染过程,但是有间接的影响,因为 JS 既可以操作 DOM 又可以操作 CSSOM,如果不等 JS 下载解析执行完以后再构建 DOM,那有可能会导致网页的有些内容出现了又消失,所以在解析 HTML 的时候,不管是行内 JS 代码还是外部 JS 代码,都会让 HTML 的解析停止下来,虽然 DOM 是可以部分解析的,但是对于这个网页来说就相当于阻塞了第一次的渲染。JS 执行完成之前什么内容都没有,在 JS 执行完了以后,下面的流程就很正常了,也就是形成渲染树(构建 DOM 后),进行布局,最后绘制。
再看下面一个例子。在请求得到 HTML 文件的时候就开始解析 HTML 文件以构建 DOM 树,解析遇到 link 标签,需要请求 css 文件,在请求 css 文件的同时,继续解析 HTML,遇到了 script 标签,需要请求 JS 文件,不过这里因为设置了 async,也就是异步执行 JS,因此不会阻塞 HTML 的解析,这时遇到了 img 标签,因此需要下载图片资源,但是不要以为图片会阻塞 HTML 解析,因此继续往下进行解析,知道把 DOM 构建完成。不过即使 DOM 构建完成也不能渲染页面,因为要等 CSSOM,因此等待解析 CSS 文件,构建 CSSOM。由于设置了 async 的 script 标签执行时间要看实际情况。假设我们就在这里执行了 JS 内容,接着就是构建渲染树,布局以及绘制了。
总结
第一步构建 DOM,第二步构建 CSSOM,第三步构建渲染树,第四步布局,第五步绘制。如果构建 DOM 的时候遇到了 JS,就请求下载执行 JS。JS 如果没有额外的设置,默认要等 CSSOM 构建完成。而 JS 既可以对 DOM,也可以对 CSSOM 进行修改,这样后面的三步又会再运行一次。
详细可见视频:浏览器渲染页面的流程
阻塞
css 在加载的时候会不会造成阻塞?css 加载不会阻塞 DOM 树的解析,css 加载会阻塞 DOM 树的渲染,css 加载会阻塞后面 js 语句的执行。Render Tree 渲染树是依赖于 dom 树和 css 规则的,所以 css 加载会阻塞 DOM 树的渲染。JavaScript 要操作 dom 节点和 css 样式,所以 js 要等 css 和 dom 渲染就位之后才能工作,所以 css 加载会阻塞后面 js 语句的执行。