浏览器渲染原理
web 的发展历史
- 超文本文档
万维网的起源,一种只有基本格式,但支持超链接的纯文本文档。(这个时候CSS和Javascript 还没有出现)
- 富媒体网页
布局(CSS)、图片、音频、视频出现
- 交互式Web应用
javascript、AJAX出现,网页转换成交互式Web应用的革命时代。揭开脚本、样式和文档之间复杂依赖关系的时代
网页的三个时代
世界上第一张网页.jpg
早期网页.jpg
现在的网页.png
什么是脚本、样式和文档之间复杂依赖关系?
首先来看一下浏览器的高层基本结构
browser components.png
浏览器的主要组件为:
1.用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户面。
2.浏览器引擎 - 在用户界面和呈现引擎之间传送指令
3.呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上
4.网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现
5.用户界面后端- 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
6.JavaScript解释器-用于解析和执行 JavaScript 代码。
7.数据存储-这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。
下面有一张非常简单的html页面,内容只有一行文本和一张图片,有一些HTML基本元素和一个CSS文件,没有引入javascript文件。那么思考一个比较直观的问题,对于这么简单的页面,浏览器是怎么把它从一个文档变成像素呈现在显示器上?
demo2.png
先撇开原理这种比较学术的字眼,直观一些地思考这个问题。HTML文档原来肯定是在服务器上,那么浏览器要显示它,肯定首先要获取到这个页面。浏览器拿到的是上图左边部分的内容,这个和最终右边的像素内容还差十万八千里,这中间它怎么处理?这时它就要认真研究拿到的HTML文档了。它有一套自己从左到右的流程。下面会说到。
**总的来说,浏览器要做的事情就是: **
1.取得资源(得先拿到HTML文档吧)
2.分析计算页面布局和渲染 (从左到右的流程)
3.javascript 执行
Q: 谁最重要?
A: 都重要(废话)。但如果一定要选取一个话,always remember that,获取资源是第一要义!巧妇难为无米之炊,没有HTML文档,任何优化技巧都是没有用的。
从左到右 / 渲染过程 / critical rendering path (关键呈现路径)
** 指浏览器所经历的一系列步骤,将HTML CSS 和 JavaScript 转换为在屏幕上呈现像素内容的过程。** 提高网页呈现速度,即优化关键呈现路径。
critical render tree.png
- 获取HTML,构建文档对象模型(DOM )
- 获取CSS,构建CSS对象模型(CSSOM)
- 二者结合,创建渲染树
- 计算布局
- 像素呈现
HTML 到 DOM(获取HTML,构建对象模型)
htmlTODOM.png
CSS到 CSSOM(CSSOM)
CSSOM.png
DOM+ CSSOM =渲染树( 只捕捉可见内容)
练习1
e1.png
很简单,首先各自建立DOM和CSS的树形结构,然后对应DOM树上的每个节点,将对应的CSS样式附着上去。注意渲染树上只捕捉可见内容,span元素是display:none,所以不显示在渲染树上,span下原来有一个文本节点,但由于CSS的继承属性,所以这个文本节点也是不可见,不显示在最终的渲染树里。
练习2
render2.png
渲染树如下。html、head、meta等元素虽然也在DOM结构里,但这些都不是可以呈现的元素,所以不会出现在最终的渲染树里。 render4.png
页面何时会渲染?
我们在讨论Web性能时有三个不同的测量指标。
- 最早渲染时间
- 文档完成时间
- 资源最后获取时间
那么这 3个时间是什么概念呢?还记得文章刚开始提到的web的发展历史吗?最初的超文本文档上根本没有图片、音视频等资源,所以它就只有文档加载时间,即浏览器拿到HTML的时间,这就是它的性能指标。当富媒体网页出现时,性能的关键指标就从文档加载时间变成了页面加载时间(Page Load Time),即PTL 。
PTL的简单定义就是:“ 浏览器中的加载旋转图标停止旋转的时间。”即浏览器中的onload事件,这个事件由浏览器在文档及其所有依赖资源(javascript、图片等)下载完毕时触发。
大家还记得曾经比较热门的一道面试题:window.onload 和$(document).ready()的区别?在这里就可以比较清楚地解释这个问题了。
- onload是浏览器的内置事件,ready是jQuery封装的事件。
- onload事件如上面所说,要等到页面上所有资源(注意不是文档,资源包括javascript,图片等等)都下载完成时才触发,而ready事件是当DOM结构准备好后就触发,即我们上面提到的DOM树形结构,一旦浏览器拿到HTML文档就会构建DOM结构。我们前面提到的三个测量指标,我们大体可以对应文档完成时间到ready,资源最后获取时间到onload.
那么什么是最早渲染时间?
用户访问一个站点时,浏览器会先发出一个请求获取html文档,拿到html后开始解析并渲染到窗口。这个过程中需要知道以下两个时间点:
Time Of First Byte
首字节时间。衡量网络性能和后端程序的一个关键指标。
Start Render Time
指用户屏幕刚开始显示某些内容的时刻。
浏览器屏幕刚开始是一大片空白,绘制时会发生一些变化,可能是显示背景、logo或者文字等。上面提到渲染树的时候说过,DOM树上会有一些非可视化的元素(例如html、head等),这些元素不会出现在最后的渲染树里,即不会呈现在窗口上。所以绘制肯定需要先解析完head元素中的内容。
一般情况下,CSS准备就绪后,即可以开始绘制。css文件通常放在head中,所以大体上可以认为浏览器开始渲染body标签的时间就是Start Render Time 。这个时间减去首字节时间,就是head标签的解析时间。
首屏时间(above the fold)
指用户在没有滚动页面的时候看到的内容渲染完成并且可以交互的时间。首屏一般指根据用户屏幕分辨率数确定一个首屏线,首屏线以上即为首屏。这个区域本身也需要时间来渲染。这个区域的资源越多(css,js,img等),其渲染时间越长,首屏时间也就越长。
首屏.png
** 哪个时间最重要?**
答案不唯一,需要根据应用而定。一般来说最好让用户能够尽早与重要内容进行交互。
这里推荐一个非常好用的在线工具 http://www.webpagetest.org/
用于显示网页加载过程的资源瀑布图。(注意看这里的很多竖线,非常详细地标注了加载过程中各个阶段的时间点)
瀑布图.png
开发者工具中的Timeline Tool 也可以看到类似信息,但没有这个来自直观和全面。开发者工具相关http://www.jianshu.com/p/e0fb716a834f
这里提一个很简单的问题:
浏览器是什么时候开始绘制页面的?
first render.png
答案:B . 绘制网页需要渲染树,构建渲染树需要DOM tree 和 CSS tree . 因此浏览器需要等到获取了CSS后再绘制网页。
Javascript,复杂依赖的源头
文章开头提到,交互式Web应用揭开了揭开脚本、样式和文档之间复杂依赖关系的时代。这个是什么意思?到目前为止,在提到Javascript之前,浏览器呈现网页的流程看起来还是很清晰的,从HTML文档里提取DOM,从CSS资源里得到CSSOM,然后得到渲染树,布局绘制,done!
然而网页要交互,必然需要JavaScript的能力。然而随之而来的,便是这个第三者带来的复杂依赖关系
Javascript 拥有操作DOM和CSS的能力。当HTML遇到<script>标签时,无法得知这个脚本将会对页面进行什么操作(增删改查),所以浏览器只好先停止处理页面,先执行js代码,然后再继续处理页面 。** JS阻塞DOM(js脚本之后的DOM)的解析和构建**。
脚本会尝试访问CSS属性,所以CSS应该在脚本执行之前先处理完成
依赖关系形成:
- JS阻塞DOM解析
- CSS阻止执行JS执行,CSS同时还阻塞渲染
于是,DOM及CSSOM的构建频繁地交织在一起:DOM构建在JS执行完毕前无法进行,而JS在CSSOM构建完成前也无法进行。获取渲染树的过程变得曲折,困难重重。而没有渲染树,自然也就没有呈现。
从这个依赖关系我们能知道什么?
- CSS阻塞渲染:渲染需要渲染树,在CSSOM完成之前,无法渲染
-
CSS阻止JS执行
渲染和脚本执行都受样式表的阻塞,所以我们必须让CSS以最快的速度下载完。所以有了流行的样式在上,脚本在下的优化原则,我们必须以最快的速度将CSS下载完成。
javascript 和 CSS 的依赖关系
如果我们外联css文件,内联js文件(js引擎的速度很快),那么这个文件里段落的颜色会是black 还是 red?
依赖1.png
答案:red.(JS必须等待CSSOM,即使它已经先于css文件到达浏览器)
关键呈现路径(critical rendering path)
指浏览器所经历的一系列步骤,将HTML CSS和JavaScript转换为在屏幕上呈现像素的过程。我们平常所说的提高网页呈现速度,也可以看做是优化关键呈现路径。
做个小练习:根据脚本画一条简单的关键呈现路径
脚本
指标依赖脚本1.png
路径图
指标依赖路径图.png
首先浏览器发出请求,获取HTML文档,然后开始解析,解析一半发现css和js资源,于是发出资源请求,获取到资源后,开始构建CSSOM,这个时候即使JS资源也已经获取到,但需要等待CSSOM的构建,CSSOM构建完以后开始执行JS,这个时候DOM构建需要停止下来等待JS执行完以后才能继续,最后渲染。即使是这么简单的网页,也会存在很多依赖条件降低关键路径的速度
关键呈现路径指标
- 关键资源数量
- 关键资源大小
- 关键路径长度(往返客户端和服务器端的次数)
CRP指标.png
从这个路径图中,我们看到整个渲染过程共有2次请求,一个是请求基本的HTML文档,一个是请求CSS文件,所以关键资源数据量有2个。关键资源大小也就是这两个文件大小之和,即9KB。来往服务器的次数也是2次。在前端开发人员能做到的范围内,提高网页呈现速度即优化关键呈现路径,既然关键呈现路径有具体的指标,那么我们在考虑优化策略的时候,就可以根据这三个具体的指标来思考。
** 一般优化策略 **
- 减小通过网络发送的数据量
- 减少关键关键资源的数量
- 缩短关键呈现路径的长度
未完待续...
参考文章:https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/
http://ju.outofmemory.cn/entry/138171