In this codelab, the performance of the following web page is improved by preloading and prefetching a few resources:
Measure
First measure how the website performs before adding any optimizations.
- To preview the site, press View App. Then press Fullscreen .
Run the Lighthouse performance audit (Lighthouse > Options > Performance) on the live version of your Glitch (see also Discover performance opportunities with Lighthouse).
Lighthouse shows the following failed audit for a resource that is fetched late:
- Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.
- Click the Network tab.
The main.css
file is not fetched by a Link element (<link>
) placed in the HTML
document, but a separate JavaScript file, fetch-css.js
, attaches
the Link element to the DOM after the window.onLoad
event. This means that the
file is only fetched after the browser finishes parsing and executing the JS
file. Similarly, a web font (K2D.woff2
) specified within main.css
is only
fetched once the CSS file has finished downloading.
The critical request chain represents the order of resources that are prioritized and fetched by the browser. For this web page, it currently looks like this:
├─┬ / (initial HTML file)
└── fetch-css.js
└── main.css
└── K2D.woff2
Since the CSS file is on the third level of the request chain, Lighthouse has identified it as a late-discovered resource.
Preload critical resources
The main.css
file is a critical asset that's needed immediately as soon as the
page is loaded. For important files like this resource that are fetched late in
your application, use a link preload tag to inform the browser to download
it sooner by adding a Link element to the head of the document.
Add a preload tag for this application:
<head>
<!-- ... -->
<link rel="preload" href="main.css" as="style">
</head>
The as
attribute is used to identify which type of resource is being
fetched, and as="style"
is used to preload stylesheet files.
Reload the application and take a look at the Network panel in DevTools.
Notice how the browser fetches the CSS file before the JavaScript responsible for fetching it has even been finished parsing. With preload, the browser knows to make a preemptive fetch for the resource with the assumption that it is critical for the web page.
If not used correctly, preload can harm performance by making unnecessary
requests for resources that aren't used. In this application, details.css
is
another CSS file located at the root of the project but is used for a separate
/details route
. To show an example of how preload can be used incorrectly, add a
preload hint for this resource as well.
<head>
<!-- ... -->
<link rel="preload" href="main.css" as="style">
<link rel="preload" href="details.css" as="style">
</head>
Reload the application and take a look at the Network panel.
A request is made to retrieve details.css
even though it is not being used by the web page.
Chrome displays a warning in the Console panel when a preloaded resource is not used by the page within a few seconds after it has loaded.
Use this warning as an indicator to identify if you have any preloaded resources that are not being used immediately by your web page. You can now remove the unnecessary preload link for this page.
<head>
<!-- ... -->
<link rel="preload" href="main.css" as="style">
<link rel="preload" href="details.css" as="style">
</head>
For a list of all the types of resources that can be fetched along with the
correct values that should be used for the as
attribute, refer to the
MDN article on Preloading.
Prefetch future resources
Prefetch is another browser hint that can be used to make a request for an asset used for a different navigation route but at a lower priority than other important assets needed for the current page.
In this website, clicking the image takes you to a separate details/
route.
A separate CSS file, details.css
, contains all the styles needed for this
simple page. Add a link element to index.html
to prefetch this resource.
<head>
<!-- ... -->
<link rel="prefetch" href="details.css">
</head>
To understand how this triggers a request for the file, open the Network panel in DevTools and uncheck the Disable cache option.
Reload the application and notice how a very low priority request is made for
details.css
after all the other files have been fetched.
With DevTools open, click the image on the website to navigate to the details
page.
Since a link element is used in details.html
to fetch details.css
, a request is made for the
resource as expected.
Click the details.css
network request in DevTools to view its details. You'll notice
that the file is retrieved from the browser's disk cache.
By taking advantage of browser idle time, prefetch makes an early request for a resource needed for a different page. This speeds up future navigation requests by allowing the browser to cache the asset sooner and serve it from the cache when needed.
Preloading and prefetching with webpack
The Reduce JavaScript payloads with code splitting post explores the use of dynamic imports to split a bundle into multiple chunks. This is demonstrated with a simple application that dynamically imports a module from Lodash when a form is submitted.
You can access the Glitch for this application here.
The following block of code, which lives in src/index.js,
is responsible for
dynamically importing the method when the button is clicked.
form.addEventListener("submit", e => {
e.preventDefault()
import('lodash.sortby')
.then(module => module.default)
.then(sortInput())
.catch(err => { alert(err) });
});
Splitting a bundle improves page loading times by
reducing its initial size. Version 4.6.0 of webpack provides support to preload or
prefetch chunks that are imported dynamically. Using this application as an
example, the lodash
method can be prefetched at browser idle time; when a user
presses the button, there is no delay for the resource to be fetched.
Use the specific webpackPrefetch
comment parameter within a dynamic import to prefetch a particular chunk.
Here is how it would look with this particular application.
form.addEventListener("submit", e => {
e.preventDefault()
import(/* webpackPrefetch: true */ 'lodash.sortby')
.then(module => module.default)
.then(sortInput())
.catch(err => { alert(err) });
});
Once the application is reloaded, webpack injects a prefetch tag for the resource into the head of the document. This can be seen in the Elements panel in DevTools.
Observing the requests in the Network panel also shows that this chunk is fetched with a low priority after all other resources have been requested.
Although prefetch makes more sense for this use case, webpack also provides support for preloading chunks that are dynamically imported.
import(/* webpackPreload: true */ 'module')
Conclusion
With this codelab, you should have a solid understanding of how preloading or prefetching certain assets can improve the user experience of your site. It is important to mention that these techniques should not be used for every resource and using them incorrectly can harm performance. The best results are noticed by only preloading or prefetching selectively.
To summarize:
- Use preload for resources that are discovered late but are critical to the current page.
- Use prefetch for resources that are needed for a future navigation route or user action.
Not all browsers currently support both preload and prefetch. This means that not all users of your application may notice performance improvements.
If you would like more information about specific aspects of how preloading and prefetching can affect your web page, refer to these articles: