什么是prefetch?
这种“资源提示” 告诉浏览器这是一个在未来可能使用到的资源。
浏览器通常会在空闲状态取得这些资源,在取得资源之后搁在HTTP缓存以便于实现将来的请求。如果有多个‘预请求提示’则会在浏览器空闲时排队执行。当浏览器离开空闲状态时正好在‘预请求’资源,那么浏览器会取消任何正在进行中的请求(同时会将部分响应数据放置在缓存中,而在Header中继续使用Content-Range字段 )并停止处理‘预请求’队列。
总之:在闲置时获取资源1
2<link rel="prefetch" href="style.css" as="style">
<link rel="prefetch" href="main.js" as="script">
webpack动态导入
我们知道webpack动态导入语法会产生代码分割,产生一个异步加载的bundle.js。懒加载可以用来优化首屏加载首屏渲染时间,但是懒加载的缺点也很明显,初次加载会有一定延迟。
webpack中的prefetch
通过动态导入的魔法注释可以指定一个模块prefetch,生成的chunks中可以区分是否prefetch
魔法注释
1
import(/* webpackPrefetch: true */ './hello');
拿到预加载模块chunkId
1
chunk.getChildIdsByOrders().prefetch
prefetch插件
步骤
- 魔法注释
- 监听html-webpack-plugin修改tag钩子,将prefetch的chunk加到head标签
实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44const HtmlWebpackPlugin = require('html-webpack-plugin');
class PrefetchPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
//准备向
// assets有source方法
compiler.hooks.compilation.tap('PrefetchPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync('PrefetchPlugin', (data, cb) => {
let chunks = compilation.chunks;
let tags = [];
chunks.forEach(chunk => {
let prefetchChunkIds = chunk.getChildIdsByOrders().prefetch;
if (prefetchChunkIds) {
prefetchChunkIds.forEach(prefetchChunkId => {
let dstChunk = chunks.filter(v => v.id == prefetchChunkId)[0]
dstChunk.files.forEach(file => {
tags.push({
tagName: 'link',
voidTag: false,
attributes: {
rel: 'prefetch',
href: file,
as: 'script'
}
});
});
});
}
});
data.headTags.push(...tags);
cb(null, data)
});
});
}
}
/**
* 获取compilation
* 知道钩子函数的参数和数据结果,加工
* htmlWebpackPluginAlterAssetTags
* 如何拿到chunk名称
*/
module.exports = PrefetchPlugin;