12.预加载,预渲染,预解析
# DNS Prefetch 与 Preconnect
# DNS Prefetch
对于浏览器未链接过的的第三方域名,将域名解析为 IP 地址通常需要 20-120 毫秒,当 DNS 解析完成后浏览器才能发起请求。如果能提前执行域的 DNS 解析,那么访问该域名时能有更快的连接速度。这能提升网站性能,尤其对于那些需要从很多第三方域加载 CDN 资源的网站。
<link rel=“prefetch”>
是尝试在请求资源之前解析域名。这可能是后面要加载的文件,也可能是用户尝试打开的链接目标。具体使用时,将 <link>
标签的 rel 属性指定为 dns-prefetch,并且在 href 属性中指定要提前进行 DNS 解析的域名。例如:
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">
# Preconnect
与<link rel=“dns-prefetch”>
不同,除了 DNS 解析之外,<link rel=“preconnect”>
还会与服务器建立 TCP 链接以及进行 TSL 握手(对 HTTPS 站点而言),这能进一步减少建立跨域请求的时间,降低延迟。现代浏览器会尽最大努力在实际请求发出之前预测站点将需要什么连接:通过启动早期“预连接”,浏览器可以提前设置必要的套接字,并从实际请求的关键路径中消除昂贵的DNS、TCP和TLS往返。也就是说,尽管现代浏览器非常智能,但它们无法可靠地预测每个网站的所有预连接目标。通过 <link rel=“preconnect”>
,跨域场景下在启动实际请求之前,可以告诉浏览器具体需要提前与哪些第三方网站建立连接。
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
虽然 preconnect 做的事比 dns-prefetch 更多,但旧版本浏览器对 preconnect 的支持程度不如 dns-prefetch。如下图所示,IE10 以及 Safari5 就已经支持 dns-prefetch ,但是 Safari 从 11.1 版本起起才支持 preconnect,并且 IE 浏览器不支持 preconnect。此外,由于与第三方域保持连接是一项比较昂贵的操作,因此 dns-prefetch 比 preconnect 更为轻量级。不建议对超过 4-6 个以上的第三方域使用 preconnect,当要加速的第三方域较多时,请使用 dns-prefetch。
# 注意事项
- dns-prefetch 仅对跨域的 DNS 解析有效,因此请避免使用它来指向您的站点或域。这是因为,到浏览器看到提示时,您站点域背后的IP已经被解析。
- 考虑将 dns-prefetch 与 preconnect一期使用。尽管 dns-prefetch 仅执行 DNS查找,但preconnect 会建立与服务器的连接。如果站点是通过HTTPS服务的,则此过程包括DNS解析,建立TCP连接以及执行TLS握手。将两者结合起来可提供进一步减少跨域请求的感知延迟的机会。如果页面需要建立与许多第三方域的连接,则将它们预先连接会适得其反,因此preconnect 提示最好仅用于最关键的连接。对于其他的,只需使用 即可节省第一步的时间 - DNS 查找。
- 不用对超链接做手动 dns prefetching,因为 chrome 会自动做 dns prefetching。 Chrome 会自动把当前页面的所有带href的link的dns都prefetch一遍。只有预计用户在后面的访问中需要用到当前页面的所有链接都不包含的域名,才需要手动需要手动添加 。Chrome一直在做类似的事情。如果您在URL栏中键入一小部分域,它将自动预解析DNS(有时甚至预呈现页面),从而减少每个请求的关键毫秒数。
- 在 Chrome 浏览器中,其他需要手动 dns prefetch 的场景:对静态资源域名做手动 dns prefetch ;对 js 里会发起的跳转做手动 dns prefetch;对重定向跳转的新域名做手动 dns prefetch 。
# Preload & Prefetch
# Preload
能让浏览器提前加载指定资源(如脚本或者样式表),并在需要执行的时候再执行。这在希望加快某个资源的加载速度时很有用。在 preload 下载完资源后,资源只是被缓存起来,浏览器不会对其执行任何操作。不执行脚本,不应用样式表。
对于这种即刻需要的资源,在页面加载的生命周期的早期阶段就希望开始获取,在浏览器的主渲染机制介入前就进行预加载。这一机制使得资源可以更早的得到加载并可用,且更不易阻塞页面的首屏渲染,进而提升性能。提供的好处主要是:
- 将加载和执行分离开,可不阻塞渲染和 document 的 onload 事件
- 提前加载指定资源,不再出现依赖的 font 字体隔了一段时间才刷出
- Preload 使开发能够自定义资源的加载逻辑,且无需忍受基于脚本的资源加载器带来的性能损失。可以使用 Preload 进行 CSS 资- 源的预加载、并且同时具备:高优先级、不阻塞渲染等特性。
- 在首屏渲染(关键CSS)中用到一些隐藏资源,比如字体,较大的图片资源等。我们可以内联关键CSS的前面使用 标签提前预加载这些重要资源:
<link rel="preload" href="fonts/cicle_fina-webfont.woff2" as="font" type="font/woff2" crossorigin>
<link rel=preload href=cat.png as=image imagesrcset="cat.png 1x, cat-2x.png 2x">
<link rel="preload" as="image" href="important.png">
2
3
注意:设置了 rel 属性的 link 标签 必须设置 as属性来声明资源的类型,否则浏览器可能无法正确加载资源。常见的 as 属性包括:
- script: JavaScript 文件
- style: CSS 样式文件
- font: 字体文件
- image: 图片文件
- fetch: 通过 fetch 或者 XHR 获取的文件,例如 ArrayBuffer 或者 JSON 文件。
使用场景:
- 字体提前加载:字体是较晚才能被发现的关键资源中常见的一种。但是在用户体验对前端来说至关重要的现阶段前端开发来说,web 字体对页面的渲染也是至关重要。字体的引用被深埋在 css 中,即便预加载器有提前解析 css,也无法确定包含字体信息的选择器是否会真正作用在 dom 节点上。所以为了减少 FOUT(无样式字体闪烁,flash of unstyled text )需要预加载字体文件。
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin />
- 动态加载脚本,但不执行:可能想在当前页去加载下一页的资源,但是在 preload 的情况下,我们常常使用动态创建 script 标签的形式,但是动态创建 script 标签的话,js 代码会立即执行。在有了preload之后,就可以做到动态加载,延迟执行。
let link = document.createElement("link");
link.href = "myscript.js";
link.rel = "preload"; // 删除这行,将立即执行
link.as = "script"; // 删除这两行,将立即执行
document.head.appendChild(link);
2
3
4
5
# Prefetch
一种浏览器机制,其利用浏览器空闲时间来下载或预取用户在不久的将来可能访问的文档。网页向浏览器提供一组预取提示,并在浏览器完成当前页面的加载后开始静默地拉取指定的文档并将其存储在缓存中。当用户访问其中一个预取文档时,便可以快速的从浏览器缓存中得到。prefetch 是一个低优先级的资源提示,允许浏览器在后台空闲时获将来可能用得到的资源,并且将他们存储在浏览器的缓存中。一旦一个页面加载完毕就会开始下载其他的资源,然后当用户点击了一个带有 prefetched 的连接,它将可以立刻从缓存中加载内容。
简单来说就是:在浏览器加载完必要的资源后,空闲时就会去获取可能需要的资源。空闲时加载,不一定会加载
<head>
...
<link rel="prefetch" href="static/img/delay_load_img.a5bb7c33.png">
...
</head>
2
3
4
5
与 dns prefetch 不同,prefetch 实际上是在请求和下载该资产,并将其存储在缓存中。但是,这取决于许多条件,因为浏览器可以忽略预取。 例如,客户端可能会在慢速网络上放弃对大字体文件的请求。
# 怎么使用
preload 和 prefetch 不会阻塞页面的 onload。preload 用来声明当前页面的关键资源,强制浏览器尽快加载 ;而 prefetch 用来声明将来可能用到的资源,在浏览器空闲时进行加载。总结如下:
- 大部分场景下无需特意使用 preload。可以通过 Chrome 的 Devtool Performance 工具,来查看阻塞页面首屏渲染的的 JS 脚本或者 CSS 样式文件,并将其设置为 preload;
- 类似字体文件这种隐藏在脚本、样式中的首屏关键资源,建议使用 preload;
- 异步加载的模块(典型的如单页系统中的非首页)建议使用 prefetch;
- 大概率即将被访问到的资源可以使用 prefetch 提升性能和体验。
webpack插件preload-webpack-plugin可以帮助我们将该过程自动化,结合htmlWebpackPlugin在构建过程中插入link标签。
const PreloadWebpackPlugin = require('preload-webpack-plugin');
...
plugins: [
new PreloadWebpackPlugin({
rel: 'preload',
as(entry) { //资源类型
if (/.css$/.test(entry)) return 'style';
if (/.woff$/.test(entry)) return 'font';
if (/.png$/.test(entry)) return 'image';
return 'script';
},
include: 'asyncChunks', // preload模块范围,还可取值'initial'|'allChunks'|'allAssets',
fileBlacklist: [/.svg/] // 资源黑名单
fileWhitelist: [/.script/] // 资源白名单
})
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PreloadWebpackPlugin 配置总体上比较简单,需要注意的是 include 属性。该属性默认取值asyncChunks,表示仅预加载异步 js 模块;如果需要预加载图片、字体等资源,则需要将其设置为allAssets,表示处理所有类型的资源。但一般情况下我们不希望把预加载范围扩得太大,所以需要通过fileBlacklist 或 fileWhitelist 进行控制
或者对于异步加载的模块,还可以通过webpack内置的/_ webpackPreload: true _/标记进行更细粒度的控制。webpack会生成标签添加到html页面头部
import(/* webpackPrefetch: true */ './componentA.vue')
import(/* webpackPreload: true */ 'lbrary');
2
懒加载优化了首屏加载的速率,prefetch预加载优化了子页面加载的速率
# Prerender
要求浏览器加载 URL 并将其渲染在不可见选项卡中。当用户单击指向该 URL 的链接时,该页面可以立即渲染。
<link rel="prerender" href="https://css-tricks.com">
就像在隐藏选项卡中打开 URL,会下载所有资源、创建DOM、布局页面、应用CSS、执行JavaScript等。如果用户导航到指定的href,则隐藏页面将切换到视图中,使其看起来立即加载。浏览器无需遵循的说明。这意味着它可以决定在内存不足或连接速度较慢时,不执行预渲染。
# 浏览器资源加载优先级规则
# 基本顺序
浏览器首先会按照资源默认的优先级确定加载顺序:
- html, css, font 这三种类型的资源优先级最高;
- 然后是 preload 资源(通过 <link rel=“preload"> 标签预加载)
- 接着是图片、语音、视频;
- 最低的是prefetch预读取的资源。
# 资源优先级提升
浏览器会按照如下规则,对优先级进行调整:
- 对于XHR请求资源:将同步 XHR 请求的优先级调整为最高。
- 对于图片资源:会根据图片是否在可见视图之内来改变优先级。图片资源的默认优先级为 Low 。现代浏览器为了提高用户首屏的体验,在渲染时会计算图片资源是否在首屏可见视图之内,在的话,会将这部分视口可见图片 ( Image in viewport ) 资源的优先级提升为 High 。
- 对于脚本资源:浏览器会将根据脚本所处的位置和属性标签分为三类,分别设置优先级。
- 首先,对于添加 defer / async 属性标签的脚本的优先级会全部降为 Low 。
- 然后,对于没有添加该属性的脚本,根据该脚本在文档中的位置是在浏览器展示的第一张图片之前还是之后,又可分为两类。在之前的 ( 标记 early ) 它会被定为High优先级,在之后的 ( 标记 late ) 会被设置为 Medium 优先级。
- 01
- 2023/07/03 00:00:00
- 02
- 2023/04/22 00:00:00
- 03
- 2023/02/16 00:00:00