从输入网址到页面呈现

问题

浏览器是大家每天使用的东西,作为前端,我们的主要工作环境也是在浏览器内,那么从我们在浏览器的输入框输入网址之后,到最终页面呈现在我们眼前,都发生了什么呢?

大概可以总结为4个步骤

  • 域名解析
  • 发起tcp的3次握手,建立tcp连接后发起http请求
  • 服务器相应http请求,返回给浏览器html(或其他)代码
  • 浏览器渲染

域名解析

域名是什么相信也不用我多说了,www.baidu.com就是一个域名,那么为什么域名需要解析呢?

因为域名就像我们的名字,通过名字并不能准确的找到一个人,但是通过身份证号就可以。对应身份证号的东西叫做ip地址。

每个域名有对应的一个或几个ip地址,那我们怎么知道某个域名对应的ip地址是什么呢?这就需要dns服务器的协助了。

但是如果每次请求都去访问dns服务器,那么服务器也是承受不住的,所以浏览器会首先在自己的缓存里找。

浏览器有自己的缓存,大概有一个小时的缓存,最大能存大概1000条。

如果自己的缓存找不到,就去系统的缓存里找,如果还是内找到,那么就去系统的host文件里找。

如果你的电脑里实在是没有这个域名对应的ip地址,那么就只能麻烦dns服务器了,但是即使这样,dns服务器还是觉得受不了,那么分吧。

dns服务器又有父子系统,从最低的dns服务器开始,看看他有没有这个域名的缓存,如果没有,那么继续往上找,直至找到为止(找到根节点,肯定能找到),找到就自己缓存一份,然后传给请求的子级或者电脑。

tcp3次握手

这个其实没有什么好说的,因为对于前端来说这个实在是用不上,那就简单说一下。

  • A:喂,你听得到我吗?A->SYN_SEND
  • B:我听得到,你听得到我吗? B->SYN_RCVD
  • A:我听得到你。 A->ESTABLISHED

还有错误状态下的4次握手

  • A:喂,我不说了。A->FIN_WAIT1
  • B:我知道了。等下,上一句还没说完。B->CLOSE_WAIT | A->FIN_WAIT2
  • B:好了,说完了,我也不说了。B->LAST_ACK
  • A:我知道了。A->TIME_WAIT | B->CLOSED

服务器相应http请求

这一段是服务端的问题,在服务端收到一个http请求后,处理内部逻辑,返回一个页面或json信息,如果返回的是html,那么浏览器开始解析。

作为前端,我们对这段东西是需要在意的,因为随着前端工程师的发展,慢慢的前端工程师接手一个叫中台的概念,这一段也需要由前端来搞了。

浏览器渲染

浏览器在接到服务端返回的html/svg/xhtml后,会去解析他们生成Dom Tree。

但是对于DOM Tree来说,他是按什么样的规则去渲染的呢?由于DOM Tree可能是一个非常大的东西,浏览器不可能等到所有的东西都下载完,render之后再呈现出来,所以浏览器通常都是render多少展示多少。

对于html代码来说就是从上到下的一步一步读取,读到标签就要停下来等一等,因为css是影响最终的渲染结果的,遇到

具体来说是

  • 浏览器读到link标签,然后去查找对应的css文件,找不到就一直找,直到超时,超时后抛弃这个css直接渲染,这就是曾经为什么github打开特别慢的原因,他老是找不到css资源。

  • 找到css之后,生成css对应的规则树,然后继续往下走,如果还有css那么继续这个步骤,新的规则按权重继续添加到规则树里,如果权重相同,那就按最后加载的为准。

  • 在这些都生成完了之后,终于开始读body了,body里的各种标签开始生成dom树,或者说生成的各个节点开始渲染,每生成一个节点,就去css的规则树里寻找对应的规则,找到就按规则渲染,没有就按默认规则渲染。这里又涉及到一个css的规则书匹配效率问题,咱们一会再说。

  • 在body都走完了或者是遇到了script标签,那么麻烦又来了,我们需要等到script运行,如果是在远程的的话,那么我们还要等待他下载,然后运行。这就造成了一个阻塞,必须等这段走完了才能往下走,当然也有非阻塞script这种奇淫技巧,但是应用是个大问题,特定环境下可以试试,这个也一会再说。

  • 为什么一定要等script运行完毕呢?因为这东西可能更改dom或者规则树了,如果不搭理他,那么最后还要返回来重新搞一遍,并且script还可能监听事件,监听的对象也可能改变,所以目前的规则都是必须等到script运行完毕后,才能继续渲染。

  • 最终找到html闭标签,最初的渲染的渲染结束了,后面不同的操作还可能触发浏览器的重绘,但是那就是另一件事情了。

下面我们返回回来说一下上面说的两个问题,一个是css的规则树匹配方式,另一个是script的非阻塞。

先说css的匹配规则,潜意识里,对于

.aa  .a {
    display:none
}

这样代码,首先匹配的应该是类名为aa的元素,再去寻找他的子元素是否是类名为a的元素的呢?还是寻找类名为a的元素有没有类名为aa的祖先呢?

上面我们说到了,css的规则树是先于dom建立的,并且dom树是一边加载一边渲染的,那么第一种思路是显然有问题的。

创建了一个类名为.aa的节点,我们并不知道他是否有一个类名为a的子节点。并且,渲染的过程,一直都是dom树在css规则里寻找是否有匹配的规则,而不是反过来。

css

图为css的规则树,从图中我们可以知道,位于后面的匹配规则其实是优先匹配的。

所以真实的情况是,浏览器发现类名的aa的开标签,但是并没有读到a的闭标签,因为他还有子元素,然后继续往下查找a的子元素,查找到类名为a的开标签和闭标签,然后通过css的规则树寻找是否有a的匹配,发现有,但是还有这条规则对a的祖先元素有要求,要求必须有类名为aa的祖先元素。这时解析器再去寻找他有没有类名为aa的祖先元素,发现有,那么匹配到这条规则并渲染。

然后继续说我们遇到另一个问题,非阻塞式的script。

script标签有一种这样的写法

也就是说,这样声明的script标签在浏览器读到他的时候,并不会等待他下载执行完成之后才会继续解析下面的dom,而是一边解析dom一边下载,并在下载完成的时候,停止dom的解析,运行脚本,等脚本运行结束,再继续解析。

还有一种写法是

由这两个特性,可以在模板引擎搞出很多有技巧的东西。

到这里,浏览网页的基本概念和过程就说完了,如果有不同的想法,欢迎交流

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值