从一道面试题,贯穿部分前端技能点

本文深入解析从输入URL到页面完全加载的过程,涵盖DNS解析、TCP三次握手、HTTP请求与响应、页面渲染流程及缓存机制等内容,并提供了多种优化手段。

从输入URL到页面加载发生了什么

基于网上的大量参考资料,梳理对这一加载流程的理解。深知自己的认知理解有限,请大家多多拍砖。

这一过程的浏览器行为表象( 步骤分解 ):

  1. 输入URL并回车 ( 通过DNS解析具体IP地址 )
  2. 浏览器发起请求 ( 浏览器通过TCP发送HTTP请求 )
  3. 服务器返回数据 ( 服务器处理请求并返回HTTP报文 )
  4. 浏览器渲染页面

输入URL并回车

浏览器主线程解析URL、通过服务器地址查找具体IP地址( DNS解析 )

DNS的解析过程:

  • 第一步:浏览器将会检查缓存中有没有这个域名对应解析过的IP地址,如果有,该解析过程将会结束。浏览器缓存是有限制的(Chrome对每个域名会默认缓存60s)
  • 第二步:如果用户的浏览器中缓存中没有,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。(开发测试环境配置hosts的原因)
  • 第三步:如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。
  • 第四步:如果hosts与本地DNS解析器缓存都没有相应的映射关系,此时会访问递归DNS服务器(区域 - 运营商 - 根服务),然后递归DNS服务器会递归查找域名记录,返回结果

注:

  1. 起初互联网信息中心是整体管理一份hosts文件,由于网络规模不断扩大不得不产生一个可以有效管理主机名和ip地址之间对应关系的系统(NDS系统) - DNS就是互联网中的分布式数据库
  2. 我们根据dns解析原理去优化dns加载速度 预加载DNS<link rel="dns-prefetch" href="/img.alicdn.com">

浏览器怎么发起请求?

浏览器是通过HTTP发起的请求,而HTTP属于TCP/IP协议族的子级,TCP/IP协议族里最重要的一点就是分层(五层)

  1. 应用层:解析成IP地址并发送HTTP请求( NDS和HTTP属于应用层 )
  2. 传输层:三次握手建立TCP连接( 为应用层提供 - 靠谱的分片数据传输 )
  3. 网络层:IP寻址( 为传输层提供传输路线 )( 在与服务器之间通过多台计算机和网络设备进行传输时,网络层起的作用就是在众多的选项内选择一条传输路线 )
  4. 链路层:网卡
  5. 物理层:物理传输 ( 硬件范畴比特流、双绞线、电磁波 .. )

什么是TCP?

TCP负责建立连接、发送数据以及断开连接。TCP提供将应用层发来的数据顺利发送至服务端的可靠传输,为了实现这一目的,需要在应用层数据前端附加一个头TCP首部( 源端口号和目标端口号、序号、校验 ),之后传递给IP层,IP层负责提供传输路线

web开发者必须熟悉http。也就是前后端的http交互 ( http报文结构、缓存、跨域、cookie、安全...) 。 http是依托于TCP的,因此...

TCP如何实现的可靠传输?( 三次握手 )

TCP协议采用了三次握手策略( 确保双方都可以收到 )。TCP链接是全双工的,双方的数据读写可以通过一个连接进行。

形象一点的描述:

// 客户端(毛蛋):二狗二狗,我是毛蛋,我是毛蛋,听到请回答,听到请回答!
// 服务端(二狗):二狗收到,二狗收到,毛蛋请讲,毛蛋请讲,完毕!
// 客户端(毛蛋):毛蛋收到, ...
复制代码
四次挥手

四次挥手:保证双发数据都发送完毕,都认为可以断开。如果客户端发送关闭请求时,服务端没有需要传递給客户端的信息时,此时可以合并为一条数据发送会客户端。

// 客户端(第一次挥手):主动发起关闭请求(FIN报文段),客户端此时进入FIN_WAIT_1状态
// 服务端(第二次挥手):回复客户端(设置ACK报文段),服务端进入CLOSE_WAIT状态,客户端收到ACK报文后,进入FIN_WAIT_2状态
// 服务端(第三次挥手):服务器此时会观察自己是否还有数据没有发送給客户端,有,先发送数据再发送FIN报文,没有,则直接发送FIN报文給客户端,同时服务端进入LAST_ACK状态
// 客户端(第四次挥手):客户端收到服务端的FIN报文段,向服务器发送ACK报文,然后客户端进入TIME_WAIT状态,服务端收到客户端的ACK报文段后,就关闭连接;此时,客户端等待2MSL后依然没有收到服务端的回复,则证明服务端已正常关闭,客户端也可以关闭这个连接了
复制代码

注:

  1. 客户端最后为什么需要等待态( TIME_WAIT )?传递的 FIN 报文段有可能会丢失
  2. 什么时候进行四次挥手:根据 Connection 请求头,如果是 Keep-alive ,服务器就保持 TCP 链接,如果没有 Keep-alive 或者主动 close ,则服务器 Response 传输完成后主动关闭 TCP 链接 ( http1.1 默认开启 Keep-alive 的,在浏览器tab关闭时,TCP链接才关闭 )

服务器返回数据

浏览器通过(HTTP-TCP-IP-链路-物理),到达服务器,服务器在通过协议进行一步步(层链路-IP-TCP-HTTP)反向解析,拿到HTTP信息后 - 服务端进行处理( 反向代理、安全拦截、跨域验证、业务逻辑... ),等待程序执行完毕后,再经过层层封装发送给前端,完成交互

各种HTTP头

通用头部

具有代表性的14个状态码
  • 200:该请求的被成功处理,请求的资源送回客户端
  • 204:该请求的被成功处理,但响应的报文信息不含主体内容( 客户端想服务端发送消息,而不需要返回新信息内容时使用 )
  • 206:范围请求(Content-Range)
  • 301:永久重定向(京东老域名 www.360buy.com)
  • 302:临时重定向( 短链接 )
  • 304:缓存( 下面有详细说明 )
  • 307:临时重定向(类似302) - 更加严谨(不会从POST变成GET)
  • 400:客户端请求有误( 请求报文存在错误语法,修改后重新发送 )
  • 401:请求未经授权
  • 403:禁止访问( 未获得访问权限、访问权限出现问题... )
  • 404:资源未找到
  • 500:服务器内部错误( 执行时发生错误、Bug、某些临时故障... )
  • 503:服务器不可用( 服务器处于超负载或停机维护状态 )

常用的请求头和响应头

缓存

两种类型:强缓存(200 from cache)与协商缓存(304)

  1. 强缓存http1.1(Cache-Control/Max-Age)、http1.0(Pragma/Expires)
  2. 协商缓存http1.1(If-None-Match/E-tag)、http1.0(If-Modified-Since/Last-Modified) http1.1的方法总是优与http1.0,主流的方案缓存这四种方案总是会都加上 更好的方案是消灭304,除了主页之外都是强缓存,需要更新时修改静态资源的MD5戳即可 http资源缓存

浏览器渲染页面

浏览器拿到HTML文档,

  1. DOM解析(标记化和树构建) - DOM树
  2. CSSS解析 - CSSOM树
  3. 将DOM树和CSSOM树合并删除渲染树(Render Tree) - 其实可以把根html看做是一个层提升(就像整体的javascript本身就是一个宏任务一样) - Paint
  4. 层叠上下文处理 - 形成Layer Tree:把渲染树中的一些节点生成单独的一层( 层提升 ) - Paint
  5. 层合并处理 - 形成Graphics Lyaer(GPU硬件加速) - GPU直接绘制
  • 附1:HTML无法用常规的自上而下或者自下而上的进行解析,因为浏览器历来对一些无效的HTML用法采用包容态度,解析过程需要不断循环往复。源内容在解析过程中通常不会改变,但是在结构中JS往往会添加额外的标记(重排 || 回流)。
  • 附2:标记化:词法分析(起始标记、结束标记、属性名、属性值)。构建树:标记生成后,传递給构件树,标记一个构建一个,如此反复
  • 附3:CSS解析与DOM解析同步进行(DOM解析结构,CSS解析样式,两者并不冲突)
  • 附4:默认DOM和CSS加载与JS互斥(JS中可能会访问我们CSS中的样式 && 结构)
  • 附5:生成Graphics Lyaer的元素(video、canvas、flash、CSS3D、perspective(透视)、opacity,transfrome应用了animation、transition时...)

说到渲染过程( 再说一说基本的优化手动 )

请求相关的优化手段

  1. 资源引入位置( 解析CSS会阻塞JS执行、解析JS会阻塞CSS解析和页面渲染,css放在头部,js放在底部 )
  2. 异步script标签

不可避免的在头部或者body中间加载JS文件, defer异步加载JS - 在DOM解析完后立即执行( 效果等同在body底部,主入口文件有一个即可 ),async异步加载JS - 在JS加载完后立即执行(建议独立的代码使用,如:baidu统计).

  1. 避免使用css@import

我的CSS代码中从来没有怎么写过,造成额外的请求,如果真的想用,使用sass,会自动合并(引入占位符不会造成代码重复)

  1. 注意空的scr

a标签空href,会重定向到当前页面地址 Form给空method,会提交表单到当前页面地址

  1. 我们根据dns解析原理去优化dns加载速度 页面加载优化<link rel="dns-prefetch" href="/img.alicdn.com">

DOM的相关的优化

  1. 缓存DOM
let CacheEl = {
  container: document.querySelectorAll('.container'),// 只有querySelectorAll返回的NodeList为静态的
  divCollection: document.getElementsByTagName('div),
  // HTMLCollection为动态
  $Box: $('#box')
};
复制代码
  1. 批量操作DOM
  1. 拼接完成后,再innerHTML更新DOM
  2. DocumentFragment对象实际上理论中能做到优化,但是
  3. 第二中方案比第一种方案时间要长,因为现代浏览器能优化的最终都会被浏览器优化
  1. DOM读写分离( 对性能影响很大 )

修改和访问DOM分开批量进行(读取DOM会触发浏览器一次渲染)

  1. 事件代理( 减少内存占用 )

其实DOM操作时代,最熟悉就是事件代理了(能动态捕获添加的节点)

  1. 最小化全局影响( 编程思维,减少内存 )

生命周期的概念:不用时清除定时器、不用时清除事件监听器、创建最小作用域(GC)、清除对象引用

资源加载优化

  1. 首屏加载之前,缩短白屏时间使用浏览器缓存( 图片、JS、CSS、字体、swf、音频、ajax GET请求 )
  2. ajax预加载场景( 短期内固定不变的数据:如 - 选项列表 )。GET请求可以被缓存。如果是POST请求可以配合localStorage做本地存储。先从本地去,如果取到了就直接用,因为页面在渲染的时候就已经请求过了。这样能达到节省时间的目的。
  3. 动态请求资源:动态创建Image对象,或者script对象,设置src,将节点append到页面(Image对象不需要),只要设置了src属性,浏览器就会发出请求。最简单的上报也是这么搞
  4. 空闲时间,为SPA的下一屏做预加载(在空闲时,悄悄的加载下一屏的内容)
  5. 预测用户操作,预先加载数据(如:亚马逊的二级菜单) - 技术要求较高
  6. 资源懒加载、图片懒加载、JS按需加载(业务逻辑比较复杂的系统,点击之后才触发对应的加载 webpack)、滚动性能提升:函数节流

总结:

  1. DNS解析
  2. TCP三次握手四次挥手
  3. 各种HTTP头信息
  4. 代表性的14种状态码
  5. 缓存
  6. 页面渲染流程
  7. 基本优化方案( webpack + 三大框架 默认优化 )

细碎点很多,有机会再继续 (⊙﹏⊙)b (⊙﹏⊙)b (⊙﹏⊙)b

  • 跨域、Cookie、CSS(BFC)、class、JS预处理、执行上下文、作用域、this、ES6...
  • web安全、https、http2.0 ...
  • Event Loop(浏览器Node)、Promise

一次dns缓存引发的惨案

BFC 神奇背后的原理

附:这篇博客 也许 想表达 恩,也许就是一篇博客 (⊙﹏⊙)b

转载于:https://juejin.im/post/5b28560051882574cb646f0c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值