Reference: http://blog.csdn.net/john1337/article/details/53579506css
本文以 Chrome 浏览器的内核 WebKit(更确切是 WebKit 分支 Blink,如下统称为 WebKit )为例,对渲染引擎如何展现页面作个简单、全面的了解。html
本文主要是两方面内容:前端
浅析浏览器内核的工做原理(以 WebKit 2 为例)。css3
浅析由浏览器内核想到的前端优化,或者说前端优化规则是从哪儿来的。git
你们知道,大部分的 WEB 页面依托浏览器呈现,而浏览器可以将页面展现出来,基本依赖于浏览器的内核,即渲染引擎。今天以 Chrome 浏览器的内核 WebKit(更确切是 WebKit 分支 Blink,如下统称为 WebKit )为例,对渲染引擎如何展现页面作个简单、全面的了解。github
浏览器的渲染引擎及其依赖模块算法
渲染引擎主要是将 WEB 资源如 HTML、CSS、图片、JavaScript等通过一系列加工,最终呈现出展现的图像。渲染引擎主要包含了对这些资源解析的处理器,如 HTML 解释器、CSS 解释器、布局计算+绘图工具、JavaScript 引擎等。为了更好地呈现渲染效果,渲染引擎还会依赖网络栈、缓存机制、绘图工具、硬件加速机制等。浏览器
浏览器的渲染过程缓存
浏览器的渲染过程,主要包括两大部分:网页资源加载过程和渲染过程。安全
上图将整个网页渲染的过程作了大体的剖析。如下咱们按照数据流向,逐一详细剖析每一个过程。
1、域名解析 DNS
当咱们在浏览器中输入 URL 后,浏览器首先会进行域名解析。通常状况下,一次 DNS 域名解析大概须要 60-120 ms,一次 TCP 的三次握手须要 1.5 个 RTT(round-trip time)。WebKit 的方案是 采用 DNS 预取技术和 TCP 预链接技术。
DNS 预取技术利用现有 DNS 机制,提早解析网页中可能的网络链接。即对用户浏览网页中存在的连接,用较少的 CPU 和网络带宽来解析这些连接的域名或 IP 地址;等用户单击连接时,就会节省时间~ 特别是域名解析慢的时候~
一样,在地址栏输入连接时,候选项也会被默默地执行 DNS 预取~。在 DNS 预取后,会预先创建 TCP 链接。
对此前端优化建议:
在页面中指定预取域名:<link rel=”dns-prefetch” href=”http://this-is-a.com”>
大数据分析,推测用户可能点击的连接,提早预取。
减小页面中的域名数量,能够直接减小DNS的请求。
2、SPDY 和 HTTP2
由于请求带来的 TCP 三次握手的 1.5 RTT 延迟,Google 引入 SPDY,尝试解决HTTP的延迟和安全性(HTTP 明文方式)问题。不过,SPDY 促使了 HTTP2.0 的诞生后,本身也再也不更新,逐步退出。
SPDY 基于 SSL 之上,轻松兼容 HTTP 新老版本。其优点以下:
多路复用。一个 TCP 链接传输多个资源。减小 TCP 链接成本。
不一样资源,不一样优先级。好比优先加载首屏。
Header 头压缩。减小传送的字节数。SPDY 对 Header 压缩率可高达 80%。
SPDY 开拓了 HTTP 新局面,秒杀咱们太多的前端优化工做,从本质上提高了页面加载速度。但咱们前端优化的工做仍是不能偏废。向着继续减小请求,减小 TCP 链接创建的路上,让咱们继续。
合并资源,如 combo 合并 JavaScript 文件、CSS 文件,利用 sprite 合并图片,图片地图等;
当页面资源较小时,可直接放页面中,如小图可以使用 Base64 编码格式引入。甚至一些基础样式,或首屏依赖样式,均可以放在页面中;
资源压缩技术。如 Gzip 等。主要是对响应数据的压缩~
精简 JavaScript 和 CSS 代码。减小无用的空格。压缩混淆~
避免连接重定向、避免错误的连接请求。创建屡次连接、屡次 DNS 解析,阻碍 DNS 预取技术。及时更新掉你页面中没有价值的连接吧。
3、资源加载
域名解析完,TCP 链接也创建起来后,资源加载器就开始工做了。
资源及资源加载器
资源包括:HTML、JavaScript、CSS 样式表、图片、SVG、字体文件、视频音频等。资源加载器有三种:
特定加载器,只加载某一种。如ImageLoader类。
缓存机制的资源加载器。特定加载器经过它查找是否有缓存资源,属于 HTML 的文档对象。
通用的资源加载器。在WebKit须要从网络或文件系统获取资源时使用。只负责获取资源的数据,被全部特定资源加载器共享。
资源加载的过程
在 WebKit 中,资源都以 CachedResource 为基类,以 Cached 为前缀,体现了浏览器的缓存机制。即请求资源时,浏览器会先看缓存中有没有这个资源,而后再决定是否向服务器发出请求。
这引出两个问题,首先,缓存资源的生命周期。
浏览器缓存不会无限增大,缓存池中的数据必然出现更替,WebKit 采用 LRU 最近最少使用算法更新缓存池数据。WebKit 遵循 HTTP 协议,当页面刷新时,判断资源是否在资源池。若存在,则附上该资源在本地的一些信息(如修改时间等),发送 HTTP 请求给服务器,服务器根据信息做出判断,若资源没更新则网络状态为 304,利用现有资源;不然执行资源加载过程。
其次,资源加载过程。
资源池中没有该资源时,执行加载过程。WebKit 能够并行(多线程)下载普通资源和 JavaScript 资源。在当前主线程被阻塞时,WebKit 会启动另外一个线程去遍历后边的网页,收集须要的资源 URL再发请求,避免阻塞。
基于资源加载,前端优化建议:
利用缓存机制,缓存经常使用且短时期内不会变动的资源,或给资源设置过时时间。
好比设置 ETag/Last-Modified 和 Expires/Cache-Control。
Expires/Cache-Control 二者做用一致,指明资源有效期,若是本地缓存还在有效期内,浏览器直接使用本地缓存,再也不发送请求。二者同时配置时,Cache-Control 高于 Expires。配置 ETag/Last-Modified 后,浏览器再次访问 URL 时,还会向服务器发送请求,确认文件是否已修改,没修改则服务器返回304,浏览器直接从本地缓存获取数据;修改过则服务器返回数据给浏览器。二者同时配置,服务器会优先检测 ETag,一致才会继续检测 Last-Modified。二者同时配置,可使服务器更准确的判断浏览器是否已有须要的缓存数据。
ETag/Last-Modified 和 Expires/Cache-Control 两对都设置时, Expires/Cache-Control 优先级更高。因此,只要本地缓存在有效期内,就不会发送请求。但页面 F5 刷新和强刷时,缓存将失效。
鉴于资源下载中可能被阻塞,将 JavaScript 文件放置页面下方。JavaScript 资源就是阻塞主线程的那个,而重建一个线程也是须要时间滴,因此把 JavaScript 扔最后吧~ 但 JavaScript 资源并不影响以前资源的加载和 DOM 树的构建。
4、从 URL 到 DOM 树的构建
当咱们拿到页面所需的资源后,渲染引擎便启动 HTML 解释器,对获取的资源进行解析处理。网页代码(字节流)通过词法分析器解码,再由语法分析器解释成词语 Token,并构建成节点 Node,直到最终构建成一棵 DOM 树。
期间,当节点为 JavaScript 节点时,将启动 JavaScript 引擎,这时将阻塞 DOM 树的构建。由于 JavaScript 执行过程当中, JavaScript 极可能会对 DOM 树进行读写操做。直到 JavaScript 执行完毕, DOM 树才会恢复构建。
其余资源并不影响 DOM 树的构建。
在前端优化中,建议将 CSS 文件放在页首,以便构建 DOM 树;而将 JavaScript 文件尽可能放在页面下方,防止阻塞构建 DOM 树;而 JavaScript 的 onload 事件里,不要写太多影响首屏渲染的、操做 DOM 树的 JavaScript 代码。
另外强调一下:
DOMContentLoaded: DOM 树构建完;
DOM 的onload事件: DOM 树构建完且网页依赖的资源都加载完了~
5、网页排版过程:由 DOM 树到构建 RenderLayer 树
这一过程,就像是页面的排版过程。它经过 CSS 样式信息,对 DOM 树进行排版,造成 RenderObject 树及 RenderLayer 树。
在 DOM 树构建完成后,WebKit 为 DOM 树节点构建 RenderObject 对象。WebKit 将根据盒模型计算节点的位置、大小等样式信息(即布局计算或排版),并将这些信息保存到对应的 RenderObject 对象。
1. CSS解释器
CSS解释过程,是从 CSS 字符串通过 CSS 解释器(CSSParser、CSSGrammer)处理后,变成渲染引擎的内部样式规则表示的过程。样式规则是解释器的输出结构,是样式匹配的输入数据。
具体过程:WebKit 在渲染元素时,CSS 解释器获取样式信息,返回匹配好的结果样式信息。每一个元素可能须要匹配不一样来源的规则,依次是用户代理(浏览器)规则集合、用户规则集合和HTML页面中包含的自定义规则集合。三者匹配方式相似。
对于每一个规则集合,先查找 ID 规则,检查有无匹配的规则,而后依次检查类型规则、标签规则等。匹配好的规则,保存到匹配结果中。WebKit 对这些规则进行排序。对于元素须要的样式属性,WebKit 选择从高优先级规则中选取,并将样式属性值返回。
2. 渲染基础:RenderObject 树
DOM 树通过布局计算、CSS parse 后,将样式信息存储在 RenderObject 对象中,并构建成 RenderObject 树。同时,WebKit 会根据网页的层次结构建立 RenderLayer 树,完成绘图上下文。DOM 树、Render 树和绘图上下文同时并存,直到页面销毁。
RenderObject 树,基于 DOM 树的一棵新树,是布局计算和渲染等机制的基础设施。
DOM 节点创建新的 RenderObject 对象的时机:
DOM 树的 Document 节点。
DOM 树的可视节点,如html、body、div 等。非可视节点如meta、head、script 等不建立。
为知足 WebKit 处理,须要创建匿名 RenderObject 节点,它不对应于 DOM 树的任何节点。如:匿名的 RenderBlock 节点。
DOM 树的每一个节点对象会递归检查是否须要建立 RenderObject,并根据 DOM 节点类型建立 RenderObject 节点;动态加入的 DOM 元素,会相应的建立 RenderObject 节点。全部这些节点构成一棵 RenderObject 树。
3. 渲染基础:网页层次和 RenderLayer 树
在 HTML 页面上,网页分层展现。目的有两个:1. 方便开发网页、设置网页的层次;2. 简化 WebKit 渲染的逻辑。
在RenderObject 树基础上,WebKit 根据须要为其中的某些节点建立新的 RenderLayer 节点,并造成一棵 RenderLayer 树。
RenderObject 节点创建新 RenderLayer 对象的时机:
DOM 树的 Document 节点对应的 RenderView 节点。
DOM 树的 Document 的子节点,即 HTML 节点对应的 RenderBlock 节点。
显式的指定 CSS 位置的 RenderObject 节点。
有透明效果的 RenderObject 节点。
节点有溢出 overflow、alpha 或反射等效果的 RenderObject 节点。
使用Canvas 2D、3D (WebGL)技术的 RenderObject 节点。
Video 节点对应的 RenderObject 节点。
RenderLayer 节点的使用能够有效减小网页结构的复杂程度,并在许多状况下能减小从新渲染的开销。
4. 布局计算及重绘时机
CSS 盒模型,是布局计算的基础;渲染引擎用来肯定如何排版元素、及元素间的位置关系。
布局计算,是针对 RenderObject 树及其子树的计算,是一种递归计算,其节点信息须要先计算其子节点的位置、大小等信息。RenderObject 对象会将计算结果存储,等待渲染时机。
每一个元素会实现本身的 layout。
页面元素定义了宽高,则按自定义宽高肯定元素大小。
文本节点等内联元素,须要结合字号大小、文字多少肯定宽高。
页面元素肯定的宽高超过了布局容器包含块提供的宽高,同时 overflow 为 visible 或 auto,WebKit 则提供滚动条保证可显示全部内容。
通常页面元素的宽高是在布局时经过计算得来。除非网页定义了页面元素的宽高。
重绘时机:只要样式发生变化,就从新计算。
首次打开页面,浏览器设置网页的可视区域,并调用计算布局的方法。可视区域改变时,网页包含块的大小也会改变,WebKit 须要从新计算布局。
网页的动画会触发布局计算。动画可能改变样式属性。
JavaScript 经过 CSSOM(CSS 对象模型) 直接修改样式,会触发 WebKit 从新计算布局。
用户交互,如滚动网页。
前端优化建议,因布局计算耗时间,一旦布局发生变化,WebKit 就须要后面的从新绘制操做。SO,减小样式的变更~减小重绘~利用 CSS3 新功能(如 CSS3 变形 translate、scale、rotate 等方法,过渡 transition 方法等)可有效提升网页的渲染效率。
6、 网页渲染过程:由 RenderLayer 树到最终的图像
在上一个过程,网页完成了 DOM 树到 RenderLayer 树的布局计算和排版处理。接下来,由渲染引擎(通常是绘图类工具)完成对 RenderLayer 树的绘制,并最终造成图像,展现给用户。
1. 绘图上下文
绘图上下文,全部的绘图操做都是在该上下文中进行的。它是一个与平台无关的抽象类,它将每一个绘图操做桥接到不一样的绘图具体实现类。
2D 绘图上下文:
提供基本绘图单元的绘制接口及设置绘图的样式。
绘图接口包括:画点、画线、画图、画多边形、画文字等。绘图样式包括颜色、线宽、字号、渐变等。
CPU 来完成 2D 操做。或用 3D 图形接口( OpenGL )完成。
3D 绘图上下文:支持 CSS3D、WebGL 等。
使用 3D 图形接口(OpenGL、Direct3D 等)
2. 渲染方式
软件渲染:CPU。一般渲染的结果是一个位图,绘制每一层时都使用该位图,区别在于位置可能不一样,每一层按从后到前的顺序。不必为每层分配一个位图,不必合成。
缺点:对 HTML5 新技术,
能力不足,CSS3D、WebGL;
性能很差,如视频、Canvas 2D;
使用率降低,特别是移动端。
优点:对更新区域处理,软件渲染可能只须要计算极小区域,硬件则须要绘制其中一层或多层,再合成。硬件代价大。
硬件加速渲染:GPU 必须有合成的步骤。分层绘制+合成。不过对于更新区域,若是只是在一个层,硬件可能会更快。
WebKit 的实现方式:
使用合适的网页分层技术、减小从新计算的布局和绘图。
使用CSS 3D 变形和动画技术。CSS 3D 变形技术,能让浏览器仅使用合成器合成全部层就能够达到动画效果。不须要布局计算和重绘~
前端优化建议:
减小重绘:由于重绘是要计算布局、绘图、合成三个阶段。其中计算布局和绘图比较费时,合成要少。
7、总结
至此,从输入 URL 到页面呈现,咱们大体作了介绍。但这只是皮毛最上方的一点,更多浏览器内核的实质,值得咱们下载一份源码,编译解析深挖~ 相信在前端优化的路上,知其然,知其因此然~ 定会走得跟远~~