浏览器中输入URL
后发生了什么?
会经过下面的主要流程:
DNS域名解析 => 建立TCP连接 => 发起HTTP请求 => 接收响应结果 => 浏览器解析HTML渲染
DNS(Domain Name Server)
用来返回某个域名对应主机的ip的服务器。
只负责提供各类顶级DNS服务器ip地址. 是域名解析的入口.
负责提供二级域名的DNS服务器IP地址. 每一个顶级域名都有对应的DNS的服务器,它们通常由专门的机构公司来维护. 比如.com
由Verisign Global Registry Services
公司维护,.edu
由Educause
公司维护. 它们各自提供自家域名下的子域名(二级域名)的名称服务. 通常我们所说的"购买域名"就是向这些公司的数据库注册一条记录.
负责提供三级域名对应的主机IP地址。由域名购买者提供,大多数域名注册公司同时提供了权威DNS托管服务。
获取域名对应的服务器ip的过程:
1、浏览器缓存
当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的IP地址(若曾经访问过该域名且没有清空缓存便存在);
2、系统Hosts
当浏览器缓存中无域名对应IP则会自动检查用户计算机系统Hosts文件是否有该域名对应IP;
3、本地域名服务器
当在用户客服端查找不到域名对应IP地址,则将进入本地DNS缓存中进行查询。通常这个本地域名DNS会配置成运营商(ISP)指定的DNS,但也不是必须的。
4、迭代查询
当以上均未完成,则会由本地DNS开始进行迭代查询:
都不行则进入根服务器进行查询。全球仅有13台根域名服务器,1个主根域名服务器,其余12为辅根域名服务器。根域名收到请求后会查看区域文件记录,若无则将其管辖范围内顶级域名(如.com)服务器IP告诉本地DNS服务器;
5、保存结果到缓存
本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时将该结果反馈给客户端,客户端通过这个IP地址与web服务器建立链接。
TCP三次握手与四次挥手
为什么tcp是三次握手不是两次
“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”
TCP连接关闭时进行四次握手过程:
客户端:“你好,我这边没有数据要传了,我要关闭咯。”
服务端:“收到~我看一下我这边有没数据要传的。”
服务端:“我这边也没有数据要传啦,我们可以关闭连接咯~”
客户端:“ok~”
这个过程会涉及到HTTP缓存的问题。
浏览器缓存可以分为两种模式,强缓存和协商缓存。
强缓存(无HTTP请求,无需协商)
直接读取本地缓存,无需跟服务端发送请求确认,http返回状态码是200(from memory cache
或者from disk cache
,不同浏览器返回的信息不一致的)。
协商缓存(有HTTP请求,需协商)
浏览器虽然发现了本地有该资源的缓存,但是不确定是否是最新的,于是想服务器询问,若服务器认为浏览器的缓存版本还可用,那么便会返回304(Not Modified) http状态码。
对应的Http header有:
Http Header | 描述 |
---|---|
Cache-Control | 指定缓存机制,优先级最高 |
Pragma | http1.0字段,已废弃,为了兼容一般使用no-cache |
Expires | http1.0字段,指定缓存的过期时间 |
Last-Modified | http1.0字段,资源最后一次的修改时间 |
ETag | 唯一标识请求资源的字符串,会覆盖Last-Modified |
接收服务器对请求处理后返回的数据,服务器具体怎么处理的,这个在前端就不具体展开了。
DOM Tree
(DOM树)CSSOM Tree
(CSS规则树)RenderObject
树(渲染树)RenderLayer
树,同时构建虚拟绘图上下文(重排)css的下载过程不会阻塞解析,但JS会等待其下载并执行完成后才会继续解析。JS下载时,会并行下载其他的资源。
webkit内核浏览器渲染过程为例:
重绘与重排(回流)
一旦渲染树构建完成,就要开始绘制(paint)页面元素了。当DOM的变化包括
导致浏览器不得不重新计算元素的几何属性,并重新构建渲染树,这个过程称为“重排”。完成重排后,要将重新构建的渲染树渲染到屏幕上,这个过程就是“重绘”。
简单的说,重排负责元素的几何属性更新,重绘负责元素的样式更新。而且,重排必然带来重绘,但是重绘未必带来重排。比如,改变某个元素的背景,这个就不涉及元素的几何属性,所以只发生重绘。
减少重绘和重排的措施包括:
var el = document.querySelector('.el'); el.style.borderLeft = '1px'; el.style.borderRight = '2px'; el.style.padding = '5px';
var el = document.querySelector('.el'); el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px'; //或者 // css .active { padding: 5px; border-left: 1px; border-right: 2px; } // javascript var el = document.querySelector('.el'); el.className = 'active';
let ul = document.querySelector('#mylist'); ul.style.display = 'none'; appendNode(ul, data); ul.style.display = 'block';
let fragment = document.createDocumentFragment(); appendNode(fragment, data); ul.appendChild(fragment);
let old = document.querySelector('#mylist'); let clone = old.cloneNode(true); appendNode(clone, data); old.parentNode.replaceChild(clone, old);
current = div.offsetLeft; div.style.left = 1 + ++current + 'px'; div.style.top = 1 + ++current + 'px';
用户输入https://www.baidu.com/
后,浏览器会判断缓存中是否由该域名的DNS信息,如果有则使用缓存中的DNS,否则查询用户本地Hosts文件,如果没有,则去ISP的DNS服务器查询IP,一般到这里就能查到了;
拿到IP后就会发起请求,此时会建立TCP连接,一个完整的TCP连接包含3次握手和4次挥手,然后发起HTTP网络请求,此时会判断是否由缓存,缓存分为强缓存和协商缓存,强缓存就是本地缓存,保存在内存或者用户磁盘中,强缓存是不会发送HTTP请求的,协商缓存则会发送HTTP请求来判断缓存是否过期,如果缓存可用,服务器会返回304状态码,接收到响应的数据后,浏览器会解析并渲染HTML,会将HTML中的DOM生成DOM树,将CSS生成CSS规则树,然后合并起来交给渲染引擎处理最后呈现到页面中,用户就可以看到界面了。
如果用户修改元素的几何属性,会触发浏览器的重排,如果用户修改元素的样式,例如颜色相关的属性则会造成重绘,重排一定会造成重绘,但重绘不一定造成重排,如果要考虑性能,那么写代码时就应该避免触发浏览器的频繁重排,例如:
改变样式的时候,最好一次性改变,可以把多个样式写成一个类名。
如果需要大量修改元素的几何位置,可以先将元素进行隐藏,修改完后显示元素,这个过程只会触发浏览器2次重排,远比一行行修改后触发几十上百次重排要划算。
其实都是为了应付面试,这种知识根本不可能深入理解的,除非你造一个mini版的浏览器,要自己实现一遍里面的各种parser和render,你不实践一遍,你怎么可能理解里面的细节呢?就像现在很多教Vue、React源码的,都开始手写一个mini版本了,就是因为你不写,光脑子里面背下那些概念,没有深入理解,屁用没有,顶多吹吹牛逼,显得很厉害,实际遇到棘手问题还是解决不了,况且,编程还是重在实践的学科。
记录一下,有时间研究怎么手写一个浏览器,哈哈,其实还是很有趣的。