跳到主要内容

网络优化

2024年03月28日
柏拉文
越努力,越幸运

一、认识


网络优化: 包括 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(预加载)和prefetch(预加载) 通常用于本页面用到的关键资源,包括关键js、字体、css文件。preload(预加载)prefetch(预加载) 的区别为:

  • preload 告诉浏览器一定会用到某个资源,浏览器会提升这个资源的加载优先级为**High**

  • prefetch 告诉浏览器可能会用到某个资源,浏览器会以一个非常低的优先级**lowest**,在空闲时间段加载这个资源

补充

一个资源的加载优先级分为五个级别,分别是

  1. Highest
  2. High
  3. Medium
  4. Low : 异步/延迟/动态插入的脚本(无论在什么位置)
  5. Lowest

4.1 preload

  1. entry1.js动态引入util.js脚本,并将webpackPreload开启
const btn = document.createElement("button");
btn.innerHTML = "动态插入";
btn.onclick = () => {
const { foo2 } = import(/* webpackPreload:true */ "./utils");
foo2();
};
document.body.append(btn);
  1. 通过@vue/preload-webpack-plugin插件向index.html模板插入Link标签
const Path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');

module.exports = {
mode: "production",
entry: {
entry1: Path.resolve(__dirname, "src", "entry1.js"),
},
output: {
filename: "[name].js",
path: Path.resolve(__dirname, "build"),
},
plugins:[
new HtmlWebpackPlugin({
template:Path.resolve(__dirname,'index.html')
}),
new PreloadWebpackPlugin()
]
};
  1. 动态插入 preload 效果如下:
Preview

4.2 prefetch

  1. entry1.js动态引入util.js脚本,并将webpackPrefetch开启
const btn = document.createElement("button");
btn.innerHTML = "动态插入";
btn.onclick = () => {
const { foo2 } = import(/* webpackPrefetch:true */ "./utils");
foo2();
};
document.body.append(btn);
  1. 动态插入 prefetch 效果如下:
Preview