java的prefetch()_Prefetch和预加载实践

本文详细介绍了Prefetch与Preload的区别,包括网络请求优先级、缓存策略、重复加载、页面跳转行为及适用场景。Prefetch适用于加载下一次导航所需的资源,而Preload用于当前页面的关键资源加载。文中还提供了在单页应用中使用Preload和Prefetch的实践策略,包括使用webpack插件和react-loadable进行预加载管理。
摘要由CSDN通过智能技术生成

之前介绍了利用Preload优化首屏关键资源的加载。今天跟大家介绍另一个性能优化手段——Prefetch。文末会结合常见工具,教大家在项目中使用Preload和Prefetch。

跟Preload类似,Prefetch也是Link的一种关系类型值,用于提示浏览器提前加载资源。跟Preload不同,Prefetch指示的是下一次导航可能需要的资源。浏览器识别到Prefetch时,应该加载该资源(且不执行),等到真正请求相同资源时,就能够得到更快的响应。

它的使用方式与Preload类似:

在HTML的

中:

通过JS动态插入:

var hint = document.createElement("link");

hint.rel = "prefetch";

hint.as = "html";

hint.href = "/article/part3.html";

document.head.appendChild(hint);

在HTTP头中:Link: ; rel=prefetch; as=image;

Prefetch的兼容性如下:

format,png

跟Preload比起来,Prefetch的兼容范围更广。唯独在Safari上对Preload的支持度比Prefetch要好。

由于Preload和Prefetch两个小朋友的名字太像了,行为也十分相似。它们站在一起的时候,很多人傻傻分不清楚。下面来说一说它俩的区别:

Prefetch vs Preload

1. 网络请求的优先级

在Chrome中,Prefetch的优先级为Lowest。而Preload的优先级则是根据as属性值所对应的资源类型来决定,总体上,Preload的优先级比Prefetch高。不过两者都不应该延迟页面的load事件。

2. 缓存策略

Preload加载的资源至少会被缓存到内存中,下一次请求的时候直接从缓存读取,从而减少从服务器加载的时间。

Prefetch的缓存并未在标准中定义,所以浏览器不保证缓存资源。不过会根据资源本身的缓存头进行相应的处理。

2017年Addy Osmani的“Preload, Prefetch and Priorities in Chrome”文章提到:

Furthermore, prefetch requests are maintained in the unspecified net-stack cache for at least 5 minutes regardless of the cachability of the resource.

意思是不论资源的缓存配置如何,Prefetch的请求会被维护在网络栈中至少5分钟。那么现在的Chrome中是不是这样呢?

笔者在Chrome 69中测试发现,如果资源配置了no-store,或者在开发者工具的网络面板中禁用缓存,浏览器并不会缓存该资源。下次请求还会再次从服务器加载资源。

format,png

(图中第一次index.js的请求是使用的Prefetch,第二次是正常请求。笔者在服务端做了延迟1s响应的处理,可以从加载时长看出第二次请求仍然是从服务器获取)

在不禁用缓存且配置了适当的缓存控制的情况下,下次请求则会从缓存加载(from disk cache),可以节约网络加载时间:

format,png

所以对于想要Prefetch的资源要做好缓存控制,以便下次请求时命中缓存。而对于动态HTML文档,则没必要使用Prefetch加载。

3. 重复加载

如果Preload的资源还在途中,此时对相同的资源再发起请求,浏览器不会重复请求资源,而是等返回了再进行处理。

而如果Prefetch的资源还在途中,再发请求,会导致二次请求(如上面“缓存策略”所示)。除此之外,有人可能会将Prefetch作为Preload的降级方案紧跟在Preload后面,也会产生两次请求,如下图所示:

format,png

format,png

4. 页面跳转时的行为

如果在当前页面跳转到下一页,在途的Preload请求会被取消。

而Prefetch的请求会在导航过程中保持,如下图所示:

document.getElementById('btn').addEventListener('click', () => {

var hint = document.createElement("link");

hint.rel = "prefetch";

hint.as = "img";

hint.href = "https://p1.ssl.qhimg.com/t01709ea6aebf12d69f.jpg";

document.head.appendChild(hint);

location.href="https://code.h5jun.com/cuma"

})

format,png

(第一个图片请求在跳转时没有被取消)

5. 适用场景

Preload的设计初衷是为了让当前页面的关键资源尽早被发现和加载,从而提升首屏渲染性能。

Prefetch是为了提前加载下一个导航所需的资源,提升下一次导航的首屏渲染性能。但也可以用来在当前页面提前加载运行过程中所需的资源,加速响应。

那么在生产环境中如何方便地使用Preload和Prefetch呢?

实践:单页应用中的Preload和Prefetch

关键资源:在单页应用中,应尽早加载关键资源。以React项目为例,应尽早加载React.js以及入口文件。如果项目使用Webpack和htmlWebpackPlugin,入口脚本文件和CSS都是直接输出到HTML中,大部分浏览器能预测解析这些资源,所以不必特意Preload这些资源。

但是有一些隐藏资源,比如font文件,则需要Preload。这种资源可以使用preloadWebpackPlugin,结合htmlWebpackPlugin,在编译阶段插入link rel="preload"标签。配置如下:

const preloadWebpackPlugin = require('preload-webpack-plugin')

...

// webpack配置

plugins: [

new htmlWebpackPlugin(),

new preloadWebpackPlugin({

as(entry) {

if (/\.woff2$/.test(entry)) return 'font';

return 'script';

},

include: 'allAssets',

rel: 'preload',

fileWhitelist: [/\.woff2/]

})

]

在HTML文件的头部会生成如下标签:

format,png

异步路由组件则应当在初始化后再加载。之前有读者朋友说异步路由组件应该用Prefetch,这个策略很好,只有一个地方需要注意:如果当前路由文件也是异步的,那么在Prefetch它的途中大概率会再次请求当前路由组件,从而导致二次加载。所以需要更细粒度的加载策略。

比如可以在鼠标移入导航菜单时再预加载其他的路由组件。既可以使用Prefetch也可以使用Preload。这里笔者认为使用Preload更优,因为Prefetch的优先级比较低而且容易引起二次加载。在React项目中,可以使用react-loadable管理异步组件的加载,它还提供了Loading状态和 preload方法:

const AboutComponent = Loadable({

loader: () => import(/* webpackChunkName: "about" */'./routes/about.js'),

loading: Loading

});

...

onMouseOver = (page) => {

AboutComponent.preload(); // 鼠标移入时再预加载相应路由组件

};

...

 {this.onMouseOver()}}>关于

其他异步模块可以用Webpack的魔法注释:import(/* webpackPrefetch: true */ "...")、 import(/* webpackPreload: true */ "...") 。有一个细节需要注意:Preload魔法注释只有写在非入口文件的chunk中才能“动态”插入 link rel="preload"标签。感兴趣的小伙伴可以试试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值