从输入url到页面加载完成发生了什么

1、浏览器的地址栏输入URL并按下回车。

我们常见的URL是这样的:http://www.baidu.com,这个域名由三部分组成:协议名、域名、端口号,这里端口是默认所以隐藏。除此之外URL还会包含一些路径、查询和其他片段,例如:http://www.tuicool.com/search?kw=%E4%。我们最常见的的协议是HTTP协议,除此之外还有加密的HTTPS协议、FTP协议、FILe协议等等。URL的中间部分为域名或者是IP,之后就是端口号了。通常端口号不常见是因为大部分的都是使用默认端口,如HTTP默认端口80,HTTPS默认端口443,FTP协议默认端口是21。

2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。

浏览器缓存,HTTP缓存有多种规则,根据是否需要重新向服务器发起请求来分类,我将其分为强制缓存,对比缓存。

强制缓存判断HTTP首部字段:cache-control,Expires。

Expires是一个绝对时间,即服务器时间。浏览器检查当前时间,如果还没到失效时间就直接使用缓存文件。但是该方法存在一个问题:服务器时间与客户端时间可能不一致。因此该字段已经很少使用。

cache-control中的max-age保存一个相对时间。例如Cache-Control: max-age = 484200,表示浏览器收到文件后,缓存在484200s内均有效。 如果同时存在cache-control和Expires,浏览器总是优先使用cache-control。

对比缓存通过HTTP的last-modified,Etag字段进行判断。

last-modified是第一次请求资源时,服务器返回的字段,表示最后一次更新的时间。下一次浏览器请求资源时就发送if-modified-since字段。服务器用本地Last-modified时间与if-modified-since时间比较,如果不一致则认为缓存已过期并返回新资源给浏览器;如果时间一致则发送304状态码,让浏览器继续使用缓存。

Etag:资源的实体标识(哈希字符串),当资源内容更新时,Etag会改变。服务器会判断Etag是否发生变化,如果变化则返回新资源,否则返回304。
  在这里插入图片描述

3、DNS解析URL对应的IP

我们知道在地址栏输入的域名并不是最后资源所在的真实位置,域名只是与IP地址的一个映射。网络服务器的IP地址那么多,我们不可能去记一串串的数字,因此域名就产生了,域名解析的过程实际是将域名还原为IP地址的过程。

首先浏览器先检查本地hosts文件是否有这个网址映射关系,如果有就调用这个IP地址映射,完成域名解析。

如果没找到则会查找本地DNS解析器缓存,如果查找到则返回。

如果还是没有找到则会查找本地DNS服务器,如果查找到则返回。

最后迭代查询,按根域服务器 ->顶级域,.cn->第二层域,hb.cn ->子域,www.hb.cn的顺序找到IP地址。
在这里插入图片描述
 递归查询,按上一级DNS服务器->上上级->…逐级向上查询找到IP地址。
 在这里插入图片描述

4、根据IP建立TCP连接(三次握手)

在通过第一步的DNS域名解析后,获取到了服务器的IP地址,在获取到IP地址后,便会开始建立一次连接,这是由TCP协议完成的,主要通过三次握手进行连接。

第一次握手: 建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;

第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。
  在这里插入图片描述

5、HTTP发起请求

完整的HTTP请求包含请求起始行、请求头部、请求主体三部分
在这里插入图片描述

6、服务器处理请求,浏览器接收HTTP响应

服务器在收到浏览器发送的HTTP请求之后,会将收到的HTTP报文封装成HTTP的Request对象,并通过不同的Web服务器进行处理,处理完的结果以HTTP的Response对象返回,主要包括状态码,响应头,响应报文三个部分。

状态码:

1:信息性状态码
100 (继续) 请求者应当继续提出请求。
101 (切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。
2:成功状态码
200:OK 请求正常处理
204:No Content 请求处理成功,但没有资源可返回
206:Partial Content 对资源的某一部分的请求
3:重定向状态码
301:Moved Permanently 永久重定向
302:Found 临时重定向
4:客户端错误状态码
400:Bad Request 请求报文中存在语法错误
403:Forbidden 访问被拒绝
404:Not Found 无法找到请求资源
5:服务器错误状态码
500:Internal Server Error 服务器端在执行时发生错误
503:Service Unavailable 服务器处于超负载或者正在进行停机维护

响应报文:

在这里插入图片描述

7、渲染页面,构建DOM树

在这里插入图片描述
页面渲染主要经过过程,具体介绍如下:

1、HTML/SVG/XHTML,解析这三种文件会产生一个 DOM Tree
2、CSS,解析 CSS 会产生 CSS 规则树
3、合并DOM Tree 和CSS规则树(CSSOM)生成渲染树(Render树)
4、浏览器根据Render树计算DOM节点位置,搭建页面结构
5、根据每个节点的属性(css)绘制HTML页面
文档对象模型(DOM树)的解析过程
字节 → 字符 → 令牌 → 节点 → 对象模型
在这里插入图片描述

1、字节转换:浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。

2、令牌化:浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌,例如,“”、“”,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则(HTML语义化)。

3、词法分析: 发出的令牌转换成定义其属性和规则的“对象”。

4、DOM 树:根据父子关联关系,构建出对应节点数(DOM树)

CSS 对象模型 (CSSOM)

CSS树的生成和DOM树的生成基本是相同,如下

在这里插入图片描述
为什么会有CSS树
在这里插入图片描述
因为CSS关系也会有父子关系,就是css中常说的继承,一些样式如font-size等子元素会继承父级,所以会生成一套对应的CSS树和DOM树相对应。

Reflow(重排)and Repaint(重绘)

重排一定会导致重绘,重绘不一定导致重排
通过上面的页面渲染介绍,很容易理解,重排就是重新布局页面结构,计算节点位置,而重绘就是绘制页面,只是一些样式如背景,颜色的变化等,不需要重新计算位置布局,所以重排一定会导致重绘,但是重绘不一定导致重排
导致页面重排主要有以下几点

1、页面首次渲染。
2、浏览器窗口大小发生改变。
3、元素尺寸(宽高等)或位置(布局属性如padding、margin)发生改变。
4、元素内容变化(文字数量或图片大小等等)。
5、元素字体大小变化。
6、添加或者删除可见的DOM元素。
7、激活CSS伪类(例如::hover)。
8、设置style属性。
9、查询某些属性或调用某些方法。
在这里插入图片描述

重排优化

分离读写

div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);

上文中触发了四次重排,通过上文介绍,offsetTop,scrollTop,clientTop等属性的修改会触发重排,当浏览器获取DOM样式的时候立刻会执行一次重排,因为需要计算浏览器位置坐标,不会再继续观察下文是否还有DOM的操作,下文介绍浏览器渲染机制。

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);

这种情况看似会触发四次重排,实际只会触发一次重排,现代浏览器基本都有渲染机制,浏览器会批量将样式修改一次性执行,批量修改完后再批量获取DOM位置,实际只触发一次
缓存DOM信息

div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';

等价于

let curLeft = div.offsetLeft;
div.style.left = curLeft + 1 + 'px';
let curTop = div.offsetTop;
div.style.top = curTop + 1 + 'px';

通过上文,容易理解这种情况会触发两次重排,可以使用缓存(实际是分离读写)来优化。

let curLeft = div.offsetLeft;
let  curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';

脱离文档

var ul = document.getElementById('demo');
for(var i = 0; i < 5; i++){
    var li = document.createElement('li');
    var text = document.createTextNode(i);
    li.appendChild(text);
    ul.appendChild(li);
}

这种情况会触发五次重排

var ul = document.getElementById('demo');
ul.style.display = 'none'; <--
for(var i = 0; i < 1e5; i++){
    var li = document.createElement('li');
    var text = document.createTextNode(i);
    li.appendChild(text);
    ul.appendChild(li);
}
ul.style.display = 'block'; <--

var ul = document.getElementById('demo');
var frg = document.createDocumentFragment(); <--
for(var i = 0; i < 1e5; i++){
    var li = document.createElement('li');
    var text = document.createTextNode(i);
    li.appendChild(text);
    frg.appendChild(li); <--
}
ul.appendChild(frg); <--

var ul = document.getElementById('demo');
var clone = ul.cloneNode(true); <--
for(var i = 0; i < 1e5; i++){
    var li = document.createElement('li');
    var text = document.createTextNode(i);
    li.appendChild(text);
    clone.appendChild(li); <--
}
ul.parentNode.replaceChild(clone,ul); <--

1、display:'none, 隐藏DOM节点;
2、创建创建一个新的空白的文档片段(DocumentFragment),它不是主DOM树的一部分,因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面重排(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。
3、cloneNode() 方法 拷贝所有属性和值。如果传递给它的参数是 true,它还将递归复制当前节点的所有子孙节点。
总结
脱离文档 => 改变演示 => 回归文档

动画脱离文档流

避免设置大量的style属性,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow,所以最好是使用class属性实现元素的动画,设置position属性,最好是设为absoulte或fixed,脱离文档流,这样不会影响其他元素的布局。

事件委托

事件委托原理:利用事件冒泡的特性,子元素都会冒泡到父元素上,当子元素如(li)元素都绑定事件时,可以只需绑定父元素(ul)来达到相同的效果,这样不仅减少了对dom的操作,减少重排或重绘,而且不用分配大量变量来保存dom,减少了内存。

window.onload = function(){
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    for(var i=0;i<aLi.length;i++){
        aLi[i].onclick = function(){
            alert(123);
        }
    }
}

委托

window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
            alert(123);
         alert(target.innerHTML);
    }
  }
}

尽量不使用table布局

不要使用table布局,因为table中某个元素一旦触发了reflow,那么整个table的元素都会触发reflow。那么在不得已使用table的场合,可以设置table-layout:auto;或者是table-layout:fixed这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围。

通过类名改样式

不要一个个修改属性,应通过一个class来修改

document.getElementsByClassName('outer')[0].style.width="50px";
document.getElementsByClassName('outer')[0].style.top="60px";

改为

document.getElementsByClassName('outer')[0].className += ' news'

8、关闭TCP连接(四次挥手)

通过四次挥手关闭连接(FIN ACK, ACK, FIN ACK, ACK)
在这里插入图片描述
第一次挥手是浏览器发完数据后,发送FIN请求断开连接。

第二次挥手是服务器发送ACK表示同意,如果在这一次服务器也发送FIN请求断开连接似乎也没有不妥,但考虑到服务器可能还有数据要发送,所以服务器发送FIN应该放在第三次挥手中。

这样浏览器需要返回ACK表示同意,也就是第四次挥手。

至此从浏览器地址栏输入URL到页面呈现到你面前的整个过程就分析完了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值