1 一道面试题展开
进行前端面试时,经常问这样一个问题:从浏览器地址栏输入URL后,到页面渲染出来,整个过程都发生了什么?
不堪一击的回答:首先浏览器发起请求,然后服务器返回数据,最后脚本执行和页面渲染。如果你的回答与上面的相差不大,就说明对前端性能优化还没有什么概念。
其实这个问题的回答可以非常细致,能从信号与系统、计算机原理、操作系统聊到网络通信、浏览器内核,再到DNS解析、负载均衡、页面渲染等多个层面回答,每个层面展开都是一个独立的章节。
这里只考虑前后端相关内容,可以划分为以下几个阶段:
1,浏览器接收到URL,到网络请求线程的开启。
2,一个完整的HTTP请求的发出。
3,服务器接收到请求并转到具体的处理后台。
4,前后台之间的HTTP交互和涉及的缓存机制。
5,浏览器接收到数据包后的关键渲染路径。
6,JS引擎的解析过程。
2 网络线程的开启
想要搞清楚这个阶段是怎么回事,先来了解下线程和进程。
a 线程和进程
简单来说,进程就是一个程序运行的实例,操作系统会为进程创建独立的内存,用来存放运行所需的代码和数据;而线程是进程的组成部分,每个进程至少有一个主线程及可能的若干子线程,这些线程由所属的进程进行启动和管理。由于多个线程可以共享操作系统为其所属的同一个进程所分配的资源,所以多线程的并行处理能有效提高程序的运行效率。如图:
特点:
(1)只要某个线程执行出错,将会导致整个进程崩溃。
(2)进程与进程之间相互隔离。这保证了当一个进程挂起或崩溃的情况发生时,并不会影响其他进程的正常运行,虽然每个进程只能访问系统分配给自己的资源,但可以通过IPC机制进行进程间通信。
(3)进程所占用的资源会在其关闭后由操作系统回收。即使进程中存在某个线程产生的内存泄漏,当进程退出时,相关的内存资源也会被回收。
(4)线程之间可以共享所属进程的数据。
问题:浏览器应该是多进程还是单进程呢?
你可以点击 Chrome 浏览器右上角的“选项”菜单,选择“更多工具”子菜单,点击“任务管理器”
可以看到多个进程在运行,浏览器为什么不使用单进程?
b 单进程浏览器
在2008年谷歌发布Chrome多进程浏览器之前,市面上几乎所有浏览器都是单进程的,它们将所有功能模块都运行在同一个进程中,如下图所示:
想象一下这样的处理方式会存在什么问题,一个进程下打开多个页面,多个窗口,内存会怎样?如果其中一个页面崩溃了,又会发生什么?一些比较明显的隐患暴露如下:
● 流畅性:首先是页面内存泄漏,浏览器内核通常非常复杂,单进程浏览器打开再关闭一个页面的操作,通常会有一些内存不能完全回收,这样随着使用时间延长,占用的内存会越来越多,从而引起浏览器运行变慢;其次由于很多模块运行在同一个线程中,如JS引擎、页面渲染及插件等,那么执行某个循环任务的模块就会阻塞其他模块的任务执行,这样难免会有卡顿的现象发生。
● 安全性:由于插件的存在,不免其中有些恶意脚本会利用浏览器漏洞来获取系统权限,进行引发安全问题的行为。
● 稳定性:由于所有模块都运行在同一个进程中,对于稍复杂的JS代码,如果页面渲染引擎崩溃,就会导致整个浏览器崩溃。同样,各种不稳定的第三方插件,也是导致浏览器崩溃的隐患。
c 多进程浏览器
出于对单进程浏览器存在问题的优化,Chrome推出了多进程浏览器架构,如图:
(1)浏览器主进程:一个浏览器只有一个主进程,负责如菜单栏、标题栏等界面显示,文件访问,前进后退,以及子进程管理等。
(2)GPU进程:GPU(图形处理单元)最初是为了实现3D的CSS效果而引入的,后来随着网页及浏览器在界面中的使用需求越来越普遍,Chrome便在架构中加入了GPU进程。可以用来提高动画渲染性能。
(3)插件进程:主进程会为每个加入浏览器的插件开辟独立的子进程,由于进程间所分配的运行资源相对独立,所以即便某个插件进程意外崩溃,也不至于对浏览器和页面造成影响。另外,出于对安全因素的考虑,这里采用了沙箱模式,在沙箱中运行的程序受到一些限制:不能读取敏感位置的数据,也不能在硬盘上写入数据。这样即使插件运行了恶意脚本,也无法获取系统权限。
(4)网络进程:负责页面的网络资源加载,之前属于浏览器主进程中的一个模块,最近才独立出来。
(5)渲染进程:也称为浏览器内核,其默认会为每个标签窗口页开辟一个独立的进程,负责将HTML、CSS和JavaScript等资源转为可交互的页面,其中包含多个子线程,即JS引擎线程、GUI渲染线程、事件触发线程、定时触发器线程、异步HTTP请求线程等。当打开一个标签页输入URL后,所发起的网络请求就是从这个进程开始的。另外,出于对安全性的考虑,渲染进程也被放入沙箱中。
多个进程之间是如何协作的?
- 首先,用户从浏览器进程里输入请求信息 (浏览器进程主要负责用户交互)
- 然后,网络进程发起 URL 请求 ;(网络进程向渲染进程和浏览器进程等提供网络下载功能)
- 服务器响应 URL 请求之后,浏览器进程就又要开始准备渲染进程 了;
- 渲染进程准备好之后,需要先向渲染进程提交页面数据,我们称之为提交文档 阶段;
- 渲染进程接收完文档信息之后,便开始解析页面和加载子资源 ,完成页面的渲染。
网络进程开启后,首先要解析url:
3 建立HTTP请求
主要任务是DNS解析和通信链路的建立,通俗点将就是我要知道需要请求资源的服务器地址。因为域名不是服务器地址是,可以理解为器地址的别名。
a DNS解析
首先查询浏览器自身的DNS缓存,如果查到IP地址就结束解析,由于缓存时间限制比较大,一般只有1分钟,同时缓存容量也有限制,所以在浏览器缓存中没找到IP地址时,就会搜索系统自身的DNS缓存;如果还未找到,接着就会尝试从系统的hosts文件中查找。
在本地主机进行的查询若都没获取到,接下来便会在本地域名服务器上查询。如果本地域名服务器没有直接的目标IP地址可供返回,则本地域名服务器便会采取迭代的方式去依次查询根域名服务器、COM顶级域名服务器和权限域名服务器等,最终将所要访问的目标服务器IP地址返回本地主机,若查询不到,则返回报错信息。
解析过程如下:
b 网络模型
国际标准化组织提出了一些网络架构模型:OSI模型、TCP/IP模型,二者的网络模型图示如图:
TCP/IP模型广泛使用,成为目前互联网事实上的标准。
c TCP链接
传输层常见的协议有TCP协议和UDP协议,这里简单介绍TCP协议的“三次握手”和“四次挥手”。
这里用图来表示“三次握手”过程:
四次挥手
链接建立好以后,就可以进行前后端交互了。
4 前后端交互
当TCP连接建立好之后,便可通过HTTP等协议进行前后端的通信,但在实际的网络访问中,并非浏览器与确定IP地址的服务器之间直接通信,往往会在中间加入反向代理服务器。
a 反向代理
反向代理服务器根据客户的请求,从后端服务器上获取资源后提供给客户端。反向代理服务器通常的作用如下:
● 负载均衡。
● 安全防火墙。
● 加密及SSL加速。
● 数据压缩。
● 解决跨域。
● 对静态资源缓存。
直观图:
b 后端处理流程
(1)首先会有一层统一的验证环节,如跨域验证、安全校验拦截等。如果发现是不符合规则的请求,则直接返回相应的拒绝报文。
(2)通过验证后才会进入具体的后台程序代码执行阶段,如具体的计算、数据库查询等。
(3)完成计算后,后台会以一个HTTP响应数据包的形式发送回请求的前端,结束本次请求。
c HTTP相关协议
HTTP 2.0到来之前,每一个资源的请求都需要开启一个TCP连接,由于TCP本身有并发数的限制,这样的结果就是,当请求的资源变多时,速度性能就会明显下降。为此,经常会采用的优化策略包括,将静态资源的请求进行多域名拆分,对于小图标或图片使用雪碧图等。
在HTTP 2.0之后,便可以在一个TCP连接上请求多个资源,分割成更小的帧请求,其速度性能便会明显上升,所以之前针对HTTP 1.1限制的优化方案也就不再需要了。
HTTP 2.0除了一个连接可请求多个资源这种多路复用的特性,还有如下一些新特性。
(1)二进制分帧:在应用层和传输层之间,新加入了一个二进制分帧层,以实现低延迟和高吞吐量。
(2)服务器端推送:以往是一个请求带来一个响应,现在服务器可以向客户端的一个请求发出多个响应,这样便可以实现服务器端主动向客户端推送的功能。
(3)设置请求优先级:服务器会根据请求所设置的优先级,来决定需要多少资源处理该请求。
(4)HTTP头部压缩:减少报文传输体积。
d 浏览器缓存
主要分为强缓存和协商缓存,在前后端交互的过程中,使用缓存可以显著的提高性能,后面会做详细介绍。
链接建立好以后,服务端需要向客户端发送文件,在这个过程中,可以将一些文件缓存到本地,提高请求的响应速度。
强缓存标识:
600再次请求改文件直接使用本地缓存,如果么有max-age 则expries生效,这是个相对时间,在这个时间到达之前,请求该文件使用本地缓存。相对时间的缺点是不精准,客户端系统时间可以更改。
协商缓存:
如果没有强制缓存标识或者强制缓存到期,查看是否有协商缓存标识:
Last-Modified / If-Modified-Since
Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间,如下。
If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源
缺点:短时间内资源发生了改变,Last-Modified 并不会发生变化。
Etag / If-None-Match(优先级较高)
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),如下。
If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,代表资源无更新,继续使用缓存文件。
5 关键路径渲染
a 构建模型
返回html文件后,需要对里面的dom和css进行解析,分为html解析和css解析,解析的过程其实就是构建DOM树的过程:
关键路径渲染
你好性能优化
上面代码的解析为:
css为:
body {
font-size: 16px;
}
p {
font-weight: bold;
}
span {
color: red;
}
p span {
display: none;
}
img {
float: left;
}
解析为:
b 渲染
(1)从所生成DOM树的根节点开始向下遍历每个子节点,忽略所有不可见的节点(脚本标记不可见、CSS隐藏不可见),因为不可见的节点不会出现在渲染树中。
(2)在CSSOM中为每个可见的子节点找到对应的规则并应用。
(3)布局阶段,根据所得到的渲染树,计算它们在设备视图中的具体位置和大小,这一步输出的是一个“盒模型”。
(4)绘制阶段,将每个节点的具体绘制方式转化为屏幕上的实际像素。
以上为输入url后发生的一系列相关内容。后续的帖子安排,就是选取本贴介绍的页面生命周期的某个局部环节进行优化,以及某些具体的优化技巧和实用工具。