好早之前的面试题,最近猛的一问,居然答不上来了。
正文
面试回答
在页面加载到最终渲染显示大致是这样的:
- 用户在浏览器输入URL回车后,浏览器为了将URL解析成IP地址,会向DNS服务器发起DNS查询,获取IP地址。
- 在建立连接后,浏览器就可以发起HTTP请求,而服务器接受请求后进行响应,浏览器从响应结果中拿到数据,并进行解析和渲染,
- 最后在用户面前就出现了一个网页。
简而言之就是三个阶段:
- 客户端发起请求阶段
- 服务端数据处理请求阶段
- 客户端页面渲染阶段
详细一些的说,大致流程如下
- 缓存解析地址,判断是否
- DNS解析,(输入的是域名)
- TCP连接
- 发送HTTP请求
- 服务器处理请求并返回需要的数据
- 浏览器解析渲染页面
- 连接结束
输入了一个域名,域名要通过DNS解析找到这个域名对应的服务器地址(ip),通过TCP请求链接服务,通过WEB服务器(apache)返回数据,浏览器根据返回数据构建DOM树,通过css渲染引擎及js解析引擎将页面渲染出来,关闭tcp连接
正常分析
首先在浏览器中输入URL ,缓存解析
浏览器先在缓存里找资源,浏览器缓存-系统缓存-路由缓存(如CDN缓存)中是否有该地址页面,如果有则显示页面内容。如果没有则进行下一步。
浏览器缓存
浏览器会记录DNS一段时间,因此,只是第一个地方解析DNS请求;
操作系统缓存
如果在浏览器缓存中不包含这个记录,则会使系统调用操作系统, 获取操作系统的记录(保存最近的DNS查询缓存);
路由器缓存
如果上述两个步骤均不能成功获取DNS记录,继续搜索路由器缓存;
ISP(互联网提供服务商)缓存
若上述均失败,继续向ISP搜索。
DNS域名解析
浏览器向DNS服务器发起请求,解析该URL中的域名对应的IP地址。
DNS服务器是基于UDP的,因此会用到UDP协议。
- DNS(Domain Name System, 域名系统),是域名和IP地址相互映射的一个分布式数据库
- DNS 解析就是从域名映射到IP地址的过程
- 如果有些网站已经访问过了,下次访问时浏览器会依次从浏览器缓存、系统缓存、路由器缓存、ISP缓存、根域名服务器、顶级域名服务器、主域名服务器里面找IP地址,所以下次访问速度更快
建立TCP连接
解析出IP地址后,根据IP地址和默认80端口,和服务器建立TCP连接。
三次握手过程
建立tcp连接就是三次握手,三次握手的过程采用 TCP 协议,其可以保证信息传输的可靠性,三次握手过程中,若一方收不到确认信号,协议会要求重新发送信号
第一次,客户端=>服务端 客户端向服务器端发送SYN=1,代表请求建立连接;还发送seq=n是客户端的序列号。
第二次,服务端=>客户端,服务端表名收到请求,发给客户端SYN=1,代表同意建立连接,ack=n+1,返回客户端序列号加1,代表确认收到信息,同时发送一个自己的序列号,seq=x代表服务端序列号。
第三次,客户端=>服务端 客户端发送SYN=0表明开始发送信息,并返回ack = x+1确认收到服务端序列号,并发送seq = n+1
发起HTTP请求
浏览器发起读取文件的HTTP请求,该请求报文作为TCP三次握手的第三次数据发送给服务器
服务器响应请求并返回结果
服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器
关闭TCP连接
通过四次挥手释放TCP连接; 前两次挥手用于关闭一个方向的数据通道,后两次挥手用于关闭另外一个方向的数据通道。
四次挥手过程
客户端向服务器发送FIN控制报文段(首部中的 FIN 比特被置位);
服务端收到FIN,回复ACK。服务器进入关闭等待状态,发送FIN;
客户端收到FIN,给服务器回复ACK,客户端进入等待状态(进入“等待”,以确保服务器收到ACK真正关闭连接);
服务端收到ACK,链接关闭。
四次挥手原因
TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。
TCP是全双工模式,这就意味着,当客户端发出FIN报文段时,只是表示客户端已经没有数据要发送了,客户端告诉服务器,它的数据已经全部发送完毕了;
但是,这个时候客户端还是可以接受来自服务端的数据;
当服务端返回ACK报文段时,表示它已经知道客户端没有数据发送了,但是服务端还是可以发送数据到客户端的;
当服务端也发送了FIN报文段时,这个时候就表示服务端也没有数据要发送了,就会告诉客户端,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。
浏览器渲染
客户端(浏览器)解析HTML内容并渲染出来,浏览器接收到数据包后的解析流程为:
构建DOM树:词法分析然后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象
构建CSS规则树:生成CSS规则树(CSS Rule Tree)
构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)
布局(Layout):计算出每个节点在屏幕中的位置
绘制(Painting):即遍历render树,并使用UI后端层绘制每个节点。
浏览器渲染详细过程
浏览器已经拿到了 server 端返回的 HTML 内容,开始解析并渲染。
最初拿到的内容就是一堆字符串,必须先结构化成计算机擅长处理的基本数据结构,这个内部结构就是 DOM,DOM 提供了对 HTML 文档的结构化表述。渲染进程通过分词器将html字节流成功成一个个 token,包括 Tag token 和文本 token。HTML 解析器维护了一个 token 栈结构,token 会按照对应顺序入栈出栈,然后将 token 解析成 DOM 节点,并将 DOM 节点添加进 DOM 树中。
异步下载资源文件
解析过程中,如果遇到< link href ="..">和< script src ="..">这种外链加载 CSS 和 JS 的标签,浏览器会异步下载,下载过程和上文中下载 HTML 的流程一样。只不过,这里下载下来的字符串是 CSS 或者 JS 格式的。
渲染样式
渲染引擎在接受到 CSS 文本时,会将 CSS 生成 CSS对象模型CSSOM(即CSS Object Model) ,通过document.styleSheets可获取所有CSS样式表,然后将 styleSheet 中的属性值进行标准化操作。最后将 DOM 和 CSSOM 整合成 RenderTree ,然后针对 RenderTree 进行渲染。
最后,渲染过程中,如果遇到< script >就停止渲染,执行 JS 代码。因为浏览器渲染和 JS 执行共用一个线程,而且这里必须是单线程操作,多线程会产生渲染 DOM 冲突。待< script >内容执行完之后,浏览器继续渲染。
—— 为何要将 CSS 放在 HTML 头部? ——
这样会让浏览器尽早拿到 CSS 尽早生成 CSSOM ,然后在解析 HTML 之后可一次性生成最终的RenderTree ,渲染一次即可。如果 CSS 放在 HTML 底部,会出现渲染卡顿的情况,影响性能和体验。
—— 为何要将 JS 放在 HTML 底部? ——
JS 放在底部可以保证让浏览器优先渲染完现有的 HTML 内容,让用户先看到内容,体验好。另外, JS 执行如果涉及 DOM 操作,得等待 DOM 解析完成才行, JS 放在底部执行时, HTML 肯定都解析成了 DOM 结构。
JS 如果放在 HTML 顶部, JS 执行的时候 HTML 还没来得及转换为 DOM 结构,可能会报错。
JS引擎解析过程
调用JS引擎执行JS代码(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等)
创建window对象
window对象也叫全局执行环境,当页面产生时就被创建,所有的全局变量和函数都属于window的属性和方法,而DOM Tree也会映射在window的doucment对象上。当关闭网页或者关闭浏览器时,全局执行环境会被销毁。
加载文件
完成js引擎分析它的语法与词法是否合法,如果合法进入预编译
预编译
在预编译的过程中,浏览器会寻找全局变量声明,把它作为window的属性加入到window对象中,并给变量赋值为’undefined’;寻找全局函数声明,把它作为window的方法加入到window对象中,并将函数体赋值给他(匿名函数是不参与预编译的,因为它是变量)。而变量提升作为不合理的地方在ES6中已经解决了,函数提升还存在。
解释执行
执行到变量就赋值,如果变量没有被定义,也就没有被预编译直接赋值,在ES5非严格模式下这个变量会成为window的一个属性,也就是成为全局变量。string、int这样的值就是直接把值放在变量的存储空间里,object对象就是把指针指向变量的存储空间。函数执行,就将函数的环境推入一个环境的栈中,执行完成后再弹出,控制权交还给之前的环境。JS作用域其实就是这样的执行流机制实现的。
结语
这个基础知识我能回答,但是细节我答不上太多,稍微显得有点丢人。
当初面试时候都记得,如今居然都记不得了。
现在趁着梳理知识体系时候,好好学习一下。