Prefetch and Preload with Handling in Webpack

prefetch and preload usage is good way to optimize website performance and user experience, this article introduce method to prefetch and preload, and fullfil with webpack

rel property of <link> tag is used to defined link type, and combined with href can prefetch and preload corresponding resource. prefetch is one of the link types

1
<link rel="prefetch" herf="URL">

preload is the other type, also use href to defined resource address, but besides handling pre fetch, it will resolve the resource, so the property as need to be added, to indicate type of resource

1
<link rel="preload" href="URL" as="MIME_TYPE">

Prefetch Resource

prefetch indicates that in the following navigation (ie: next webpage), user may need to use corresponding resource, and give a hint to browser that corresponding resource need to be gotten when idle

First create a new diretory prefetch-preload-demo (all the code in this article will create here), install the associated dependencies, and create new directory static

mkdir prefetch-preload-demo
cd prefetch-preload-demo
npm init -y
npm i -D http-server
mkdir static

In static directory, create prefetch.htmlmain.js and script.js

prefetch.html defined a link with prefetch as rel

1
2
3
4
5
6
7
8
9
10
11
<html>
<head>
<title>Prefetch</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="prefetch" href="script.js">
</head>
<body>
<script src="main.js"></script>
</body>
</html>

main.js create a button, and bind click event

1
2
3
4
5
6
7
8
let button = document.createElement('button');
button.innerHTML = 'Add Script';
button.addEventListener('click', e => {
let script = document.createElement("script");
script.src = "script.js";
document.head.appendChild(script);
});
document.body.appendChild(button);

script.js just simply print

1
console.log('script run');

Run the sever (or add server script in package.json)

npx http-server

Access http://localhost:8080 and navigate to static, click prefetch.html, or directly visit online webpage, in the initial status, check the network tab of devtool, as the following image (don’t check Disable Cache, click the gear on the right, check Use large request rows)

  • script.js is fetched, the two number in the size column, 275 B shows the download size, 0 B shows the resolved size (which means that it’s not been resolved yet)
  • Console tab is empty, which means script has not run yet

Click Add Script on the page, <script> tag with address script.js will be added on the page, and the following content will been added on the network tab

  • Download size become (prefetch cache), which means that the resource is directly gotten from prefetch cache, and the resolved size below is no longer 0
  • The debug content is printed on the console, which means that the script is resolved and run just now

Preload Resource

preload indicates that in the current navigation (mostly the current webpage), user very likely need to use corresponding resource, and give a hint to browser that corresponding resource need to be gotten preferentially

Change prefetch in link tag of prefetch.html to preload, and set resource type as to script, which form preload.html

1
<link rel="preload" href="script.js" as="script">

Access the local server corresponding prefetch.html, or directly access online webpage, in the initial status, check the network tag of devtool

  • script.js is downloaded preferentially, the resolved size of size column is no longer 0, namely preload no only download the script, but also resolve it
  • The console tab is still empty, namely although the script is resolved, it has not run yet

Click Add Script, there is not additional record in the network tab, but the console output the print content of script

  • Because script is resolved completely, there is even no need to fetch from cache, just directly run
  • If Add Script is not clicked in 3 second, the console will output warning, because the resource which should be loaded preferentially is not used in time

    The resource https://chanvinxiao.com/demo/html/script.js was preloaded using link preload but not used within a few seconds from the window’s load event. Please make sure it has an appropriate as value and it is preloaded intentionally.

webpack associated handling

Run the following command to install corresponding dependencies, and create new directory src

npm i -D webpack webpack-cli html-webpack-plugin preload-webpack-plugin@3.0.0-beta.4
mkdir src

  • Current version of PreloadWebpackPlugin is 2.x, which is not compatible with current version of webpack which is 4.x, so need to specify the version as the newest 3.x beta

copy main.js and script.js to src, and modify the click event handler in main.js to the following

1
2
3
button.addEventListener('click', e => {
import(/* webpackChunkName: "script" */ './script.js');
});
  • import() will load script dynamically, webpack will generate code which create script tag similar to above
  • The comment in import is magical comment with special meaning, and if webpackChunkName is not set, the imported script will name with number sequence

Add webpack.config.js as following

1
2
3
4
5
6
7
8
9
10
11
12
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');

module.exports = {
entry: './src/main.js',
plugins: [
new HtmlWebpackPlugin({
filename: 'preload.html'
}),
new PreloadWebpackPlugin()
]
}
  • HtmlWebpackPlugin will generate corresponding html file automatically, index.html as default, here we change it via option filename
  • PreloadWebpackPlugin is plugin of HtmlWebpackPlugin, it will add link tag with link type as preload for the dynamically imported resource, and the as value can be determined by the suffix

PreloadWebpackPlugin also support prefetch, with rel option as prefetch

1
2
3
4
5
6
new HtmlWebpackPlugin({
filename: 'prefetch.html'
}),
new PreloadWebpackPlugin({
rel: 'prefetch'
})

But if we want to generate both preload.html and prefetch.html, option excludeHtmlNames need to be set in the corresponding PreloadWebpackPlugin configuration, or else both preload and prefetch link tags will be produced simultaneously

1
2
3
4
5
6
7
8
9
10
11
12
13
new HtmlWebpackPlugin({
filename: 'preload.html'
}),
new HtmlWebpackPlugin({
filename: 'prefetch.html'
}),
new PreloadWebpackPlugin({
excludeHtmlNames: ['prefetch.html']
}),
new PreloadWebpackPlugin({
rel: 'prefetch',
excludeHtmlNames: ['preload.html']
})

Build files (or add build script in package.json)

npx webpack

prefetch.html and preload.html will be generated in dist directory, access local server corresponding address, you will get the same effect as the above static page

Summarize

This article show the fulfillment of prefetch and preload with two methods: static page and webpack building. The complete code can be found in GitHub, the main technical points are as follow:

  • ELEMENT.appendChild create script dynamically
  • import() load script dynamically and set the magical comment
  • The configuration of html-webpack-plugin and its plugin