文章目录
1、浏览器上输入url
- 用户输入url,例如http://www.baidu.com。
- 其中http为协议,www.baidu.com为网络地址,及指出需要的资源在那台计算机上。
- 一般网络地址可以为域名或IP地址,此处为域名。
- 使用域名是为了方便记忆,但是为了让计算机理解这个地址还需要把它解析为IP地址。
2、应用层DNS解析域名
什么是DNS?
- DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。
- 通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。
通俗的讲,我们更习惯于记住一个网站的名字,比如www.baidu.com,而不是记住它的ip地址,比如:167.23.10.2。而计算机更擅长记住网站的ip地址,而不是像www.baidu.com等链接。因为,DNS就相当于一个电话本,比如你要找www.baidu.com这个域名,那我翻一翻我的电话本,我就知道,哦,它的电话(ip)是167.23.10.2。
DNS解析过程:
- 浏览器先检查自身缓存中有没有被解析过的这个域名对应的 IP 地址。
- 浏览器缓存中没有命中,浏览器会检查操作系统缓存中有没有对应的已解析过的结果。(在 Windows 中可通过 C 盘里一个叫 hosts 的文件来设置,如果你在这里指定了一个域名对应的 IP 地址,那浏览器会首先使用这个 IP 地址。)
- 至此还没有命中域名,会请求本地域名服务器(LDNS)来解析这个域名,这台服务器一般在你的城市的某个角落,距离你不会很远,并且这台服务器的性能都很好,一般都会缓存域名解析结果,大约 80% 的域名解析到这里就会完成。
- LDNS 仍然没有命中,就直接跳到 Root Server 域名服务器请求解析。
- 根域名服务器返回给 LDNS 一个所查询域的主域名服务器(gTLD Server,国际顶尖域名服务器,如.com .cn .org 等)地址。
- 此时 LDNS 再发送请求给上一步返回的 gTLD Server。
- 接受请求的 gTLD Server 查找并返回这个域名对应的 Name Server 的地址,这个 Name Server 就是网站注册的域名服务器。
- Name Server 根据映射关系表找到目标 IP,返回给 LDNS。LDNS 缓存这个域名和对应的 IP。
- LDNS 把解析的结果返回给用户,用户根据 TTL 值缓存到本地系统缓存中,域名解析过程至此结束。
3、应用层客户端发送HTTP请求
互联网内各网络设备间的通信都遵循TCP/IP协议,利用TCP/IP协议族进行网络通信时,会通过分层顺序与对方进行通信。
分层由高到低分别为:应用层、传输层、网络层、数据链路层。发送端从应用层往下走,接收端从数据链路层网上走。
得到 IP 地址后,浏览器会开始构造一个 HTTP 请求,应用层客户端向服务器端发送的HTTP请求包括:请求行
、请求头
、请求正文
。
请求消息格式:
请求行(方法 空格/URL 空格 协议/版本号)
消息报头 可选
空行 发送回车符和换行符,通知服务器以下不再有请求头。
消息正文 可选
请求行
- 包括:请求方法开头,以空格分开,后面跟着请求的URI和协议的版本。
eg:
GET /index.html HTTP/1.1
POST /index.html HTTP/1.1
Delete /index.html HTTP/1.1
请求的方法:
- GET 请求获取URI所标识的资源
- POST 请求URI所标识的资源,并请求服务器接收附加在请求后面的数据,常用于表单提交。
- HEAD 请求获取由URI所标识的资源的响应消息报头
- PUT 请求服务器存储一个资源,并用URI作为其标识
- DELETE 请求服务器删除URI所标识的资源
- TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断
- CONNECT 保留将来使用
- OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求
请求头
- 请求头包含请求的附加信息,由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。
常见的请求头如下:
Accept
:指定浏览器或其他客户程序能够处理的MIME类型:request.getHeader(“Accept”);Accept-Charse
: 使用的字符集,如ISO-8859-1Accept-Encoding
:客户端能够处理的编码类型,如gzip或compressAccept-Language
:客户端的首选语言Authorization
:客户用这个报头来标识自己的身份Connnection
:标明客户是否能够处理持续性HTTP连接。持续性连接允许客户或者浏览器在单个socket中读取多个文件,从而节省协商几个独立连接所需的开销Content-Length
:只适用于POST请求,用来给定POST数据的大小,以字节为单位:request.getContentLengthCookie
:向服务器返回cookie,这些cookie是之前由服务器发送给浏览器的:request.getCookiesHost
:标明原始URL中给出的主机名和端口号If-Modified-Since
:仅当页面在指定日期之后发生改变的情况下,客户程序才希望获取该页面。如果没有更新的结果,则服务器发送304报头。这个选项十分有用,因为使用它,浏览器可以缓存文档,只在它们发生改变时才通过网络重新载入它们Referer
:标明引用Web页面的URLUser-Agent
:求的浏览器或者其他客户程序
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://localhost/
请求正文
- 可以承载多个请求参数的数据,包含回车符、换行符和请求数据,并不是所有请求都具有请求数据
username=detanx&password=Aa123456 // 请求数据
HTTP 请求是纯文本格式的,所以在 TCP 的数据段中可以直接分析 HTTP 文本的。
4、传输层TCP传输报文
- 位于传输层的TCP协议为传输报文提供可靠的字节流服务。
- 它为了方便传输,将大块的数据分割成以报文段为单位的数据包进行管理,并为它们编号,方便服务器接收时能准确地还原报文信息。
- TCP协议通过"
三次握手
"等方法保证传输的安全可靠。
5、网络层IP协议查询MAC地址
- IP协议的作用是把TCP分割好的各种数据包传送给接收方。
- 要保证确实能传到接收方还需要接收方的MAC地址,也就是物理地址。
- IP地址和MAC地址是一一对应的关系,一个网络设备的IP地址可以更换,但是MAC地址一般是固定不变的。
- ARP协议可以将IP地址解析成对应的MAC地址。当通信的双方不在同一个局域网时,需要多次中转才能到达最终的目标,在中转的过程中需要通过下一个中转站的MAC地址来搜索下一个中转目标。
6、数据到达数据链路层
在找到对方的MAC地址后,就将数据发送到数据链路层传输。这时,客户端发送请求的阶段结束。
7、服务器接收数据
- 接收端的服务器在链路层接收到数据包,再层层向上直到应用层。
- 这过程中包括在运输层通过TCP协议将分段的数据包重新组成原来的HTTP请求报文。
8、服务器响应请求,并返回http报文
服务接收到客户端发送的HTTP请求后,查找客户端请求的资源,并返回响应报文。
HTTP响应三部分:状态行
、响应头
、响应正文
。
状态行
- 状态行由 3 部分组成,分别为:协议版本、状态码、状态码描述。
- 各元素之间以空格分隔。
- 其中协议版本与请求报文一致,状态码描述是对状态的简单描述。
HTTP/1.1 200 OK // 状态行
常见状态码:
200 OK
:客户端请求成功302 Move temporarily
:请求的资源现在临时从不同的 URI 响应请求。400 Bad Request
:客户端请求有语法错误,不能被服务器所理解401 Unauthorized
: 请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用403 Forbidden
:服务器收到请求,但是拒绝提供服务404 Not Found
:请求资源不存在,eg:输入了错误的URL500 Internal Server Error
: 服务器发生不可预期的错误503 Server Unavailable
:服务器当前不能处理客户端的请求,一段时间后可能恢复正常
响应头
响应头与请求头一样,只是与请求头包含的附加信息有所不同。
常见响应报头
Allow
: 指定服务器支持的请求方法(GET,POST等)Cache-Control
: 告诉浏览器或者其他客户,什么环境可以安全地缓存文档Connection
: close值,指定浏览器不用使用持续性的HTTP连接Content-Disposition
: 要求浏览器询问客户,将响应存储在磁盘上给定名称的文件中Content-Encoding
: 标明页面在传输过程中所使用的编码方式Content-Language
: 文档使用的语言Content-Length
: 响应中的字节Content-Type
: MIMEExpires
: 规定内容的过期时间,从而不再需要继续缓存:response.setDataHeader(“Expires”, Time)Last-Modified
: 标明文件最后的修改时间Location
: 300-399之间的所有响应都应该包括这个报头,它通知浏览器文档的地址Refresh
: 标明浏览器应该多长时间之后请求最新的页面:response.setIntHeader(“Refresh”, 30)Set-Cookie
: 指定一个同页面相关的cookie
// 响应头
Date: Sun, 17 Mar 2013 08:12:54 GMT
Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 4393
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json
相应数据
- 根据请求类型的不同,响应的数据格式也有所不同,有可能是二进制文件流、JSON 对象、字符串、HTML 文件等。
请求头和响应头是浏览器和服务器之间交互的重要信息,由程序自动处理,通常不需要人为干预。
9、浏览器解析渲染页面
浏览器使用流式布局模型 (Flow Based Layout)。
- css的加载和解析不会阻塞html文档的解析
- css的解析会阻塞js的执行,必须等到CSSOM生成后才能执行js
- js的执行会阻塞html文档的解析
- html一边解析一边显示
- css必须完全解析完毕才能进入生成渲染树环节
浏览器渲染过程
- 浏览器把获取到的html代码解析成1个
Dom树
,html中的每个tag都是Dom树中的1个节点,根节点就是我们常用的document对象 。dom树里面包含了所有的html tag,包括display:none隐藏,还有用JS动态添加的元素等。 - 浏览器把所有样式(主要包括css和浏览器的样式设置)解析成
样式结构体(cssom)
,在解析的过程中会去掉浏览器不能识别的样式,比如IE会去掉-moz开头的样式. - dom tree和样式结构体(cssom)结合后构建
呈现树(render tree)
,render tree有点类似于dom tree,但其实区别有很大,render tree能识别样式,render tree中每个node都有自己的style,而且render tree不包含隐藏的节点(比如display:none的节点,还有head节点),因为这些节点不会用于呈现,而且不会影响呈现的,所以就不会包含到 render tree中。visibility:hidden隐藏的元素还是会包含到render tree中的,因visibility:hidden 会影响布局(layout),会占有空间。 - Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
- Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素。
- Display:将像素发送给GPU,展示在页面上。
有了RenderTree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置,最后把节点绘制到页面上。
由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,但table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一。
回流必将引起重绘,重绘不一定会引起回流。
回流(reflow)
当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
一个元素的回流可能会导致了其所有子元素以及DOM中紧随其后的节点、祖先节点元素的随后的回流。
导致回流的操作:
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化
- 添加或者删除可见的DOM元素
- 激活CSS伪类(例如::hover)
- 查询某些属性或调用某些方法
一些常用且会导致回流的属性和方法:
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- scrollIntoView()、scrollIntoViewIfNeeded()
- getComputedStyle()
- getBoundingClientRect()
- scrollTo()
重绘(repain)
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
var s = document.body.style;
s.padding = "2px"; // 回流+重绘
s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 再一次重绘
s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
// 添加node,再一次 回流+重绘
document.body.appendChild(document.createTextNode('abc!'));
///可以看到每次DOM元素的样式操作都会引发重绘,如果涉及布局还会引发回流。
回流、重绘性能影响分析
回流比重绘的代价要更高。
有时即使仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。
现代浏览器会对频繁的回流或重绘操作进行优化:
- 浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。
当你访问以下属性或方法时,浏览器会立刻清空队列:
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- width、height
- getComputedStyle()
- getBoundingClientRect()
避免回流、重绘
CSS
- 使用 transform 替代 top
- 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)
- 避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。
- 尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。
- 避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。
- 将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame
- 避免使用CSS表达式,可能会引发回流。
- 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-change、video、iframe等标签,浏览器会自动将该节点变为图层。
- CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。
JavaScript
- 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
- 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
- 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
- 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
- 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。