一个非常巨大的问题,涉及的领域好多……
稍微理了下主干,当然不是我自己理的,综合了很多前辈的文章,列表如下:
http://web.jobbole.com/94150/
https://www.cnblogs.com/daijinxue/p/6640153.html
https://segmentfault.com/a/1190000006879700
https://segmentfault.com/a/1190000003925803
https://blog.csdn.net/u012194956/article/details/79110212
从输入url到页面加载发生了什么
总览
- 浏览器地址栏输入URL并回车
- 浏览器查找当前URL是否存在缓存,并比较缓存是否过期
- DNS解析URL对应的IP
- 根据IP建立TCP连接(三次握手)
- 发送HTTP请求
- 服务器处理请求,浏览器接受HTTP响应
- 浏览器解析并渲染页面
- 关闭TCP连接(四次握手)
浏览器进程/线程模型,JS的运行机制
多进程的浏览器
浏览器是多进程的,可能包括:
- 浏览器主进程:负责协调、主控,只有一个
- 浏览器渲染进程(内核):每个Tab页面有一个互不影响的进程,负责页面渲染、脚本执行、事件处理等(有时候会优化,多个空白Tab会合并成一个进程)
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
- GPU进程:最多一个,用于3D绘制
多线程的浏览器内核
每个Tab的浏览器内核进程是多线程的,包括:
- GUI渲染线程
- JS引擎线程
- 事件触发线程
- 定时触发器线程
- 异步HTTP请求线程
JS引擎是浏览器内核进程中的一个单线程。
解析URL
URL的几大部分:
- protocal 协议头:HTTP,HTTPS,FTP
- host 主机域名或IP地址
- port 端口号,http默认80,https默认443
- path 目录路径
- query 查询参数
- fragment #后的哈希值,一般用来定位到某个位置
HTTP与HTTPS
HTTP的缺点:
- 通信使用明文,内容可能被窃听
- 不验证通信方身份,可能遭遇伪装
- 无法验证报文的完整性,有可能被篡改
HTTP+加密+认证+完整性保护=HTTPS
- HTTPS是身披SSL外壳的HTTP:通常情况下HTTP是直接和TCP层进行通信的。当使用SSL(安全套接字)时,则演变成HTTP先和SSL通信,SSL再和TCP通信。
SSL
- 对称加密:加密和解密用同一个密钥,易被截取密钥
- 非对称加密(公开密钥加密):发送密文一方使用对方的共有密钥进行加密处理,对方收到加密信息后,再使用自己的私有密钥进行解密。
- HTTPS采用二者结合的方式,因为非对称加密相比对称加密处理速度较慢**,所以使用非对称加密传输对称加密的共享密钥,再使用共享密钥进行通信。** -** 公开密钥的认证是要钱的**!私有密钥保存在服务器端。
SSL/TLS握手流程
1. 浏览器请求建立SSL链接,并向服务端发送一个随机数–Client random和客户端支持的加密方法,比如RSA加密,此时是明文传输。
2. 服务端从中选出一组加密算法与Hash算法,回复一个随机数–Server random,并将自己的身份信息以证书的形式发回给浏览器 (证书里包含了网站地址,非对称加密的公钥,以及证书颁发机构等信息) 3. 浏览器收到服务端的证书后 - 验证证书的合法性(颁发机构是否合法,证书中包含的网址是否和正在访问的一样),如果证书信任,则浏览器会显示一个小锁头,否则会有提示 - 用户接收证书后(不管信不信任),浏览会生产新的随机数–Premaster secret,然后证书中的公钥以及指定的加密方法加密`Premaster secret`,发送给服务器。 - 利用Client random、Server random和Premaster secret通过一定的算法生成HTTP链接数据传输的对称加密key-`session key` - 使用约定好的HASH算法计算握手消息,并使用生成的`session key`对消息进行加密,最后将之前生成的所有信息发送给服务端。 4. 服务端收到浏览器的回复 - 利用已知的加解密方式与自己的私钥进行解密,获取`Premaster secret` - 和浏览器相同规则生成`session key` - 使用`session key`解密浏览器发来的握手消息,并验证Hash是否与浏览器发来的一致 - 使用`session key`加密一段握手消息,发送给浏览器 5. 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,
之后所有的https通信数据将由之前浏览器生成的session key并利用对称加密算法进行加密
HTTPS的好处
- SEO:搜索引擎排名更高
- 安全性:
- 使用https协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
- https协议是由SSL+http协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
- https是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。
HTTPS的缺点
- 使页面加载时间延长50%,增加10%-20%的好点
- 影响缓存,郑家数据开销
- 加密范围有限,在黑客攻击、拒绝服务攻击、浏览器劫持方面没什么作用
- SSL整数的信用链体系并不安全
- 费用
- 握手协议费时
- 占用服务器资环高
浏览器缓存
强制缓存(不需要向浏览器发起请求)判断HTTP首部字段:cache-control,Expires。
- cache-control中的max-age保存一个相对时间。例如Cache-Control: max-age = 484200,表示浏览器收到文件后,缓存在484200s内均有效。
- Expires是一个绝对时间,即服务器时间。浏览器检查当前时间,如果还没到失效时间就直接使用缓存文件。但是该方法存在一个问题:服务器时间与客户端时间可能不一致。因此该字段已经很少使用。
- 如果同时存在cache-control和Expires,浏览器总是优先使用cache-control。
对比缓存(需要向服务器发送请求)通过HTTP的last-modified,Etag字段进行判断
-
last-modified是第一次请求资源时,服务器返回的字段,表示最后一次更新的时间。下一次浏览器请求资源时就发送if-modified-since字段。服务器用本地Last-modified时间与if-modified-since时间比较,如果不一致则认为缓存已过期并返回新资源给浏览器;如果时间一致则发送304状态码,让浏览器继续使用缓存。
-
Etag:资源的实体标识(哈希字符串),当资源内容更新时,Etag会改变。服务器会判断Etag是否发生变化,如果变化则返回新资源,否则返回304。
DNS解析
通过域名查找目标文件所在主机的IP地址 查找过程: 查找过程
-
浏览器搜索自己的 DNS 缓存(维护一张域名与 IP 地址的对应表);
-
搜索操作系统中的 DNS 缓存(维护一张域名与 IP 地址的对应表);
-
搜索操作系统的 hosts 文件( Windows 环境下,维护一张域名与 IP 地址的对应表);
-
操作系统将域名发送至 LDNS**(本地区域名服务器**,如果你在学校接入互联网,则 LDNS 服务器就在学校,如果通过电信接入互联网,则 LDNS 服务器就在你当地的电信那里。)LDNS 查询 自己的 DNS 缓存(一般查找成功率在 80% 左右),查找成功则返回结果,失败则发起一个迭代 DNS 解析请求;
-
LDNS 向 Root Name Server (根域名服务器,其虽然没有每个域名的的具体信息,但存储了负责每个域,如 com、net、org等的解析的顶级域名服务器的地址)发起请求,此处,Root Name Server 返回 com 域的顶级域名服务器的地址;
-
LDNS 向 com 域的顶级域名服务器发起请求,返回 baidu.com 域名服务器地址;
-
LDNS 向baidu.com 域名服务器发起请求,得到 www.baidu.com 的 IP 地址;
-
-
LDNS 将得到的 IP 地址返回给操作系统,同时自己也将 IP 地址缓存起来;
-
操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来;
-
至此,浏览器已经得到了域名对应的 IP 地址。
DNS优化
DNS缓存
NS存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。
DNS负载均衡
DNS可以返回一个合适的机器的IP给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是DNS负载均衡,又叫做DNS重定向。
TCP连接(三次握手)
上图部分标志说明:
(1)ACK:TCP规定,只有当ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1
(2)SYN(SYNchronization):在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这个是一个连接请求报文。对方若同意建立连接,则响应报文中SYN=1,ACK=1。因此,SYN置1表示这是一个连接请求或连接接受报文。
(3)FIN(finish):终结的意思,用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已经发送完毕,并请求释放连接。
三次握手
(1)第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置1,序列号seq(sequence number)为x;然后,客户端进入SYN_SEND状态,等待服务器的确认。
(2)第二次握手:服务器收到SYN报文段服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,ACK位置1,确认号ack(acknowledgement number)为x+1;同时,自己还要发送SYN请求信息,将SYN位置1,序列号seq为y;服务器将上述SYN+ACK报文段一并发送给客户端,此时服务器进入SYN_RECV状态
(3)第三次握手:客户端收到服务器的SYN+ACK报文段:然后将确认号ack设置为y+1,向服务器发送ACK报文段。这个报文段发送完毕后,客户端和服务器都进入ESTABLISHED状态,完成TCP三次握手,之后可以开始传数据 !
HTTP请求
- HTTP请求报文是由三部分组成: 请求行, 请求报头和请求正文。
请求行
Method Request-URL HTTP-Version CRLF eg: GET index.html HTTP/1.1
- 常用的方法有: GET, POST, PUT, DELETE, OPTIONS, HEAD。
请求报头
- 请求报头允许客户端向服务器传递请求的附加信息和客户端自身的信息。
- 常见的请求报头有: Accept, Accept-Charset, Accept-Encoding, Accept-Language, Content-Type, Authorization, Cookie, User-Agent等。
- Accept用于指定客户端用于接受哪些类型的信息,Accept-Encoding与Accept类似,它用于指定接受的编码方式。Connection设置为Keep-alive用于告诉客户端本次HTTP请求结束之后并不需要关闭TCP连接,这样可以使下次HTTP请求使用相同的TCP通道,节省TCP连接建立的时间。
请求正文
当使用POST, PUT等方法时,通常需要客户端向服务器传递数据。这些数据就储存在请求正文中。在请求包头中有一些与请求正文相关的信息,例如: 现在的Web应用通常采用Rest架构,请求的数据格式一般为json。这时就需要设置Content-Type: application/json。
五层因特尔协议栈
1.应用层(dns,http) DNS解析成IP并发送http请求
2.传输层(tcp,udp) 建立tcp连接(三次握手)
3.网络层(IP,ARP) IP寻址
4.数据链路层(PPP) 封装成帧
5.物理层(利用物理介质传输比特流) 物理传输(然后传输的时候通过双绞线,电磁波等各种介质) 从客户端发出http请求到服务器接收,中间会经过一系列的流程。
- 从应用层的发送http请求
- 到传输层通过三次握手建立tcp/ip连接
- 再到网络层的ip寻址
- 再到数据链路层的封装成帧
- 最后到物理层的利用物理介质传输。
- ISO七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
http1.0 1.1 2 的区别
长连接与连接
首先看tcp/ip层面的定义:
- 长连接:一个tcp/ip连接上可以连续发送多个数据包,在tcp连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持(类似于心跳包)
- 短连接:通信双方有数据交互时,就建立一个tcp连接,数据发送完成后,则断开此tcp连接
http1.0,http1.1
- http1.0中,默认使用的是短连接,也就是说,浏览器没进行一次http操作,就建立一次连接,任务结束就中断连接,譬如每一个静态资源请求时都是一个单独的连接
- http1.1起,默认使用长连接,使用长连接会有这一行Connection: keep-alive,在长连接的情况下,当一个网页打开完成后,客户端和服务端之间用于传输http的tcp连接不会关闭,如果客户端再次访问这个服务器的页面,会继续使用这一条已经建立的连接
- keep-alive不会永远保持,它有一个持续时间,一般在服务器中配置(如apache),另外长连接需要客户端和服务器都支持时才有效
HTTP2.0
HTTP2.0 与http1.1
- http1.1中,每请求一个资源,都是需要开启一个tcp/ip连接的,所以对应的结果是,每一个资源对应一个tcp/ip请求,由于tcp/ip本身有并发数限制,所以当资源一多,速度就显著慢下来
- http2.0中,一个tcp/ip请求可以请求多个资源,也就是说,只要一次tcp/ip请求,就可以请求若干个资源,分割成更小的帧请求,速度明显提升。
HTTP2.0特性
- 多路复用(一个tcp/ip可以请求多个资源)
- 首部压缩(HTTP头部压缩,减少体积)
- 二进制分帧(在应用层和传输层之间增加二进制分帧层,改进传输性能,实现低延迟和高吞吐量)
- 服务端推送(服务端可以对客户端的一个请求发出多个响应,可以主动通知客户端)
- 请求优先级(如果流被赋予了优先级,它就会基于这个优先级来处理,由服务器决定需要多少资源来处理该请求。)
服务器处理请求并返回HTTP报文
通用头部(状态码)
Request Url: 请求的web服务器地址
Request Method: 请求方式 (Get、POST、OPTIONS、PUT、HEAD、DELETE、CONNECT、TRACE) Status Code: 请求的返回状态码,如200代表成功 Remote Address: 请求的远程服务器地址(会转为IP)
1xx:指示信息–表示请求已接收,继续处理。
2xx:成功–表示请求已被成功接收、理解、接受。
3xx:重定向–要完成请求必须进行更进一步的操作。
4xx:客户端错误–请求有语法错误或请求无法实现。
5xx:服务器端错误–服务器未能实现合法的请求。
- 平时遇到比较常见的状态码有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500(分别表示什么请自行查找)。
响应报头,响应报文
- 常见的响应报头字段有: Server, Connection...。
- 服务器返回给浏览器的文本信息,通常HTML, CSS, JS, 图片等文件就放在这一部分。
cookie session
浏览器获取html,解析,渲染
过程如下
- 解析HTML,生成DOM树
- 解析CSS,生成CSS规则树
- 合并CSS和DOM数,生成render树
- 布局render树(layout/reflow),负责各元素尺寸、位置的计算
- 绘制render树(paint),绘制页面像素信息
- 浏览器将各层信息发送给GPU,GPU将各层合成,显示在屏幕上
HTML解析,构建DOM
- Bytes → characters → tokens → nodes → DOM
-
Conversion转换:浏览器将获得的HTML内容(Bytes)基于他的编码转换为单个字符
-
Tokenizing分词:浏览器按照HTML规范标准将这些字符转换为不同的标记token。每个token都有自己独特的含义以及规则集
-
Lexing词法分析:分词的结果是得到一堆的token,此时把他们转换为对象,这些对象分别定义他们的属性和规则
-
DOM构建:因为HTML标记定义的就是不同标签之间的关系,这个关系就像是一个树形结构一样 例如:body对象的父节点就是HTML对象,然后段略p对象的父节点就是body对象
生成CSSOM树
过程同上
构建渲染树
-
一般来说,渲染树和DOM树相对应的,但不是严格意义上的一一对应
-
因为有一些不可见的DOM元素不会插入到渲染树中,如head这种不可见的标签或者display: none等
渲染
-
计算CSS样式
-
构建渲染树
-
布局(回流,Layout,Reflow),主要定位坐标和大小,是否换行,各种position overflow z-index属性
-
绘制(重绘,Repaint),将图像绘制出来
回流与重绘
- Layout,也称为Reflow,即回流。一般意味着元素的内容、结构、位置或尺寸发生了变化,需要重新计算样式和渲染树
- Repaint,即重绘。意味着元素发生的改变只是影响了元素的一些外观之类的时候(例如,背景色,边框颜色,文字颜色等),此时只需要应用新样式绘制这个元素就可以了
什么会引起回流
1.页面渲染初始化
2.DOM结构改变,比如删除了某个节点
3.render树变化,比如减少了padding
4.窗口resize
5.最复杂的一种:获取某些属性,引发回流, 很多浏览器会对回流做优化,会等到数量足够时做一次批处理回流, 但是除了render树的直接变化,当获取一些属性时,浏览器为了获得正确的值也会触发回流,这样使得浏览器优化无效,包括 (1)offset(Top/Left/Width/Height) (2) scroll(Top/Left/Width/Height) (3) cilent(Top/Left/Width/Height) (4) width,height (5) 调用了getComputedStyle()或者IE的currentStyle
- 元素几何属性变化,margin,padding,height,width,border
- 回流一定伴随着重绘,重绘却可以单独出现
优化方案
- 减少逐项更改样式,最好一次性更改style,或者将样式定义为class并一次性更新
- 避免循环操作dom,创建一个documentFragment或div,在它上面应用所有DOM操作,最后再把它添加到window.document
- (1)DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。不过它有一种特殊的行为,该行为使得它非常有用,即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作。其实他就是一个游离在DOM树外面的容器,所以你在把它插入文档节点之前,随便给他增删节点都不会引起回流
- (2)使用display:none,只引发两次回流和重绘。道理跟上面的一样。因为display:none的元素不会出现在render树
- (3)使用cloneNode和replaceChild技术,引发一次回流和重绘(这条其实没太明白)
- 避免多次读取offset等属性。无法避免则将它们缓存到变量
- 将复杂的元素绝对定位或固定定位,使得它脱离文档流,否则回流代价会很高
- 尽量不要使用表格布局,如果没有定宽表格一列的宽度由最宽的一列决定,那么很可能在最后一行的宽度超出之前的列宽,引起整体回流造成table可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。
资源外链的下载
遇到外链时的处理
- 遇到外链时,会单独开启一个下载线程去下载资源(http1.1中是每一个资源的下载都要开启一个http请求,对应一个tcp/ip链接)
遇到CSS样式资源
CSS资源的处理有几个特点:
- CSS下载时异步,不会阻塞浏览器构建DOM树
- 会阻塞渲染,也就是在构建render时,会等到css下载解析完毕后才进行(这点与浏览器优化有关,防止css规则不断改变,避免了重复的构建)
- media query声明的CSS是不会阻塞渲染的
遇到JS脚本资源
JS脚本资源的处理有几个特点:
- 阻塞浏览器的解析,也就是说发现一个外链脚本时,需等待脚本下载完成并执行后才会继续解析HTML
- 浏览器的优化,一般现代浏览器有优化,在脚本阻塞时,也会继续下载其它资源(当然有并发上限),但是虽然脚本可以并行下载,解析过程仍然是阻塞的,也就是说必须这个脚本执行完毕后才会接下来的解析,并行下载只是一种优化而已
- defer与async,普通的脚本是会阻塞浏览器解析的,但是可以加上defer或async属性,这样脚本就变成异步了,可以等到解析完毕后再执行 详见异步加载JS
遇到img图片类资源
- 遇到图片等资源时,直接就是异步下载,不会阻塞解析,下载完毕后直接用图片替换原有src的地方
loaded和domcontentloaded
简单的对比:
- DOMContentLoaded: 事件触发时,仅当DOM加载完成,不包括样式表,图片(譬如如果有async加载的脚本就不一定完成)
- load: 事件触发时,页面上所有的DOM,样式表,脚本,图片都已经加载完成了
怎么提高CSS加载速度?
- 使用CDN
- 将CSS压缩
- 合理使用缓存
- 减少HTTP请求数,多个CSS合并
JS引擎解析过程
JS的解释阶段
- JS是解释型语音,所以它无需提前编译,而是由解释器实时运行
- 核心的JIT编译器将源码编译成机器码运行
JS的预处理阶段
- 分号补全
- 变量提升
JS的执行阶段
- 执行上下文,执行堆栈概念(如全局上下文,当前活动上下文)
- VO(变量对象)和AO(活动对象)
- 作用域链
- this机制等
断开TCP连接(四次挥手)
- 第一次挥手是浏览器发完数据后,发送FIN请求断开连接。
- 第二次挥手是服务器发送ACK表示同意,如果在这一次服务器也发送FIN请求断开连接似乎也没有不妥,但考虑到服务器可能还有数据要发送,所以服务器发送FIN应该放在第三次挥手中。
- 这样浏览器需要返回ACK表示同意,也就是第四次挥手
注:
-
为什么服务器在接到断开请求时不立即同意断开:当服务器收到断开连接的请求时,可能仍然有数据未发送完毕,所有服务器先发送确认信号,等所有数据发送完毕后再同意断开。
-
第四次握手后,主机发送确认信号后并没有立即断开连接,而是等待了 2 个报文传送周期,原因是:如果第四次握手的确认信息丢失,服务器将会重新发送第三次握手的断开连接的信号,而服务器发觉丢包与重新发送的断开连接到达主机的时间正好为 2 个报文传输周期。
-