跳到主要内容

网络优化

2023年12月23日
柏拉文
越努力,越幸运

一、认识


网络优化: 包括 HTTP2DNS 预解析、PreloadPrefetch等手段

二、HTTP2


传统的 HTTP 1.1 存在队头阻塞的问题,同一个 TCP 管道中同一时刻只能处理一个 HTTP 请求,也就是说如果当前请求没有处理完,其它的请求都处于阻塞状态,另外浏览器对于同一域名下的并发请求数量都有限制,比如 Chrome 中只允许 6 个请求并发(这个数量不允许用户配置),也就是说请求数量超过 6 个时,多出来的请求只能排队、等待发送。

因此,在 HTTP 1.1 协议中,队头阻塞和请求排队问题很容易成为网络层的性能瓶颈。而 HTTP 2 的诞生就是为了解决这些问题,它主要实现了如下的能力:

  • 多路复用: 将数据分为多个二进制帧,多个请求和响应的数据帧在同一个 TCP 通道进行传输,解决了之前的队头阻塞问题。而与此同时,在 HTTP2 协议下,浏览器不再有同域名的并发请求数量限制,因此请求排队问题也得到了解决。

  • Server Push: 即服务端推送能力。可以让某些资源能够提前到达浏览器,比如对于一个 html 的请求,通过 HTTP 2 我们可以同时将相应的 jscss 资源推送到浏览器,省去了后续请求的开销。

三、DNS 预解析


浏览器在向跨域的服务器发送请求时,首先会进行 DNS 解析,将服务器域名解析为对应的 IP 地址。我们通过 dns-prefetch 技术将这一过程提前,降低 DNS 解析的延迟时间,具体使用方式如下:

<!-- href 为需要预解析的域名 -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">

一般情况下 dns-prefetch 会与 preconnect 搭配使用,前者用来解析 DNS,而后者用来会建立与服务器的连接,建立 TCP 通道及进行 TLS 握手,进一步降低请求延迟。使用方式如下所示:

<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
<link rel="dns-prefetch" href="https://fonts.gstatic.com/">

值得注意的是,对于 preconnectlink 标签一般需要加上 crorssorigin(跨域标识),否则对于一些字体资源 preconnect 会失效。

四、Preload/Prefetch


对于一些比较重要的资源,我们可以通过 Preload 方式进行预加载,即在资源使用之前就进行加载,而不是在用到的时候才进行加载,这样可以使资源更早地到达浏览器。具体使用方式如下:

<link rel="preload" href="style.css" as="style">
<link rel="preload" href="main.js" as="script">

其中我们一般会声明 hrefas 属性,分别表示资源地址和资源类型。Preload的浏览器兼容性也比较好,目前 90% 以上的浏览器已经支持。

与普通 script 标签不同的是,对于原生 ESM 模块,浏览器提供了 modulepreload 来进行预加载:

<link rel="modulepreload" href="/src/app.js" />

仅有 70% 左右的浏览器支持这个特性,不过在 Vite 中我们可以通过配置一键开启 modulepreloadPolyfill,从而在使所有支持原生 ESM 的浏览器(占比 90% 以上)都能使用该特性,配置方式如下:

// vite.config.ts
export default {
build: {
polyfillModulePreload: true
}
}

除了 PreloadPrefetch 也是一个比较常用的优化方式,它相当于告诉浏览器空闲的时候去预加载其它页面的资源,比如对于 A 页面中插入了这样的 link 标签:

<link rel="prefetch" href="https://B.com/index.js" as="script">

这样浏览器会在 A 页面加载完毕之后去加载B这个域名下的资源,如果用户跳转到了B页面中,浏览器会直接使用预加载好的资源,从而提升 B 页面的加载速度。而相比 PreloadPrefetch 的浏览器兼容性不太乐观