我们都知道 “输入 url 并回车后” 或者说 “向服务端发请求” 浏览器并不是直接将请求内容送达服务端,这里有一步 “url解析,进行 dns 查找 ip 地址”

DNS优化

DNS也是有缓存的 —— 如果之前你解析过这个域名,会在本地/某个dns服务器上有缓存(一定时间内)。
DNS解析有三种方法:
递归查询:客户端向DNS服务器A查询,A向B查询、B向C查询。C将结果返回给B、B将结果返回给A,A将结果返回给客户端;
迭代查询:客户端向DNS服务器A查询,A说不知道,你去找B、然后客户端向DNS服务器B查询…
非递归查询:客户端查询某一个DNS服务器,服务器返回结果中可能有记录,也可能没有。但不管如何,DNS本次查询结束;
常用的查询方式是 递归+迭代:在本地使用递归查询;如果本地没有的话用迭代查询方式查找配置域名服务器、根域名服务器、顶级域名服务器。
经过测试,如果没有配置 dns,预计每一次 dns解析 时间在 20~120ms,这是浪费性能的。我们有两种优化方式:
减少DNS请求次数;
DNS预获取(prefetch)
对于第一种方式,其实就是 “一个页面上尽可能少用不同的域名”,或者说“资源都放在相同的服务器上”。
这么做当然可以,但无异于“因小失大”:在实际项目中往往会将不同资源放在不同的服务器上!

多个服务器的好处

比如你可以将 API 数据接口/数据库放在一个服务器上、将图片/音视频放在一个服务器上、将第三方服务放在一个服务器上。
抗压能力
这样做虽然增加了 DNS解析 所需要的时间,但是一方面能提高资源的合理利用:web服务器、数据服务器、图片服务器…它们所承受的压力是不一样的,我们可以有选择性的买不同性能的服务器,也能节省费用。
另一方面也能提高 HTTP并发 :你也许不知道,不同浏览器都会对同域名的请求有一个“最大并发数限制”:

HTTP并发限制

HTTP客户端一般对同一个服务器的并发连接个数都是有限制的。
实际上,浏览器确实使用并行连接,但它们将并行连接的总数限制为少量(通常为四个)。服务器可以自由地关闭来自特定客户端的过多连接。
一个HTTP 连接请求在同一时间只能被一个线程访问。
HttpClient使用一个叫做Http连接管理器的特殊实体类来管理Http连接。Http连接管理器在新建HTTP连接时,作为工厂类;管理持久的 http连接 的生命周期;同步持久连接(确保线程安全,即一个HTTP连接同一时间只能被一个线程访问)。
如果一个 Http连接 被释放或者被它的消费者明确表示要关闭,那么底层的连接就会和它的代理进行分离,并且该连接会被交还给连接管理器。这时,即使服务消费者仍然持有代理的引用,它也不能再执行 I/O操作,或者更改 Http连接 的状态。
但是我们如果用多个服务器,也就是多个域名,就能避开这个限制!
为什么要用预获取?
DNS prefetch其实就是利用了 HTTP的并发性。它会把要访问的服务器(域名)提前 prefetch。这么做的好处是页面加载的时候,在还没加载 DOM结构 的时候,就把 link 请求发过去了,发出去以后就开始做 dns解析,解析回来的结果存在本地。
它是加载DOM结构的时候同时进行DNS操作的。
我们可以在HTML的head中添加meta元素的方式做dns优化:

  1. <meta http-equiv="x-dns-prefetch-control" content="on">
  2. <link rel="dns-prefetch" href="//域名1">
  3. <link rel="dns-prefetch" href="//域名2">
  4. <link rel="dns-prefetch" href="//域名3">

CDN优化

CDN (全称 Content Delivery Network),即内容分发网络。
它是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术。
简单来讲,CDN就是根据用户位置分配最近的资源。
于是,用户在上网的时候不用直接访问源站,而是访问离他“最近的”一个 CDN 节点,术语叫「边缘节点」,其实就是缓存了源站内容的代理服务器。
cdn的原理
在没有应用CDN时,我们使用域名访问某一个站点时的路径是“ 用户提交域名→浏览器对域名进行解释→DNS 解析得到目的主机的IP地址→根据IP地址访问发出请求→得到请求数据并回复 ”;
应用CDN后,DNS 返回的不再是 IP 地址,而是一个CNAME(Canonical Name ) 别名记录,指向CDN的全局负载均衡;
CNAME实际上在域名解析的过程中承担了中间人(或者说代理)的角色,这是CDN实现的关键!
CNAME本质是一种DNS记录,它的作用是将一个域名映射到另一个域名。域名解析的时候如果看到CNAME记录,则会从映射目标重新开始查询。

cdn缓存应用

使用第三方CDN服务:针对公司没有条件购买自己的CDN服务,如:https://www.bootcdn.cn/
CDN进行静态资源缓存:比如 JavaScript、css、图片,甚至是将脚本和webpack结合直接放到CDN,按成一键自动部署整个项目。
在页面中使用 webp 格式的图片。

其他

1.如何更快请求:

域名发散,同时避免不必要的cookie传输,但会需要更多的dns查询——用DNS预解析/预链接(预创建TCP链接)
http2多路复用
cdn 资源服务器离用户更近,多个资源服务器,一个中心服务器,更高容错性
http缓存,适用于重复资源,注意资源更新,新鲜度检测/强缓存/服务器再验证/协商缓存/正常请求
service worker缓存 控制页面发送请求的次数,用cache storage缓存请求,可离线运行,

2.何时请求:资源声明

资源不仅只有js/css,还有如img,可以用资源提示:preload/prefetch/prerender
预加载preload:使用高优先级加载
预提取prefetch:在空闲时加载
预渲染prerender:在空闲时加载并渲染html
后两个多用于加载接下来可能会用到的内容,比如翻页,但兼容性比较差;
加载的功能不会马上应用,只会缓存下来,所以使用时还是要声明资源;
预加载的内容,如果没有使用,会警告,这是对带宽的浪费

chrome资源优先级:先css再js
1.html、 css、 font、 同步XHR请求
2.preload资源、脚本、异步XHR请求、可见图片
3.异步脚本、图片、音频、视频
4.prefetch资源
异步js脚本加载 async /defer:
将script放在body最后,虽然可以避免dom还没解析,脚本操作就结束的错误,但在总时间上,耗时很长

defer会等dom解析完成之后再执行js
async一边解析dom,一边执行js
async可以用来加载与dom无关的脚本
其余脚本应该都用defer

另:http2服务器推送,本意是减少链接请求,无需声明就推送其他资源,但有了cdn/缓存等机制,浏览器获取资源不一定要直接通过服务器,设置这个功能反而浪费了带宽,且影响下一次链接,也不利于后续维护