Parcel 2: Asset Preloading and Prefetching
See original GitHub issueModern browsers support Resource Hints, which allow declaratively specifying resources that a web page will need in the future. This is done by inserting <link>
tags in the document head, which allows browsers to discover these resources before they normally would and prioritize downloading and caching them, thus speeding up page loads and subsequent navigations. For example, resources like web fonts and background images referenced in a CSS file could be preloaded rather than waiting for the CSS file to be loaded and parsed to start downloading them. Code split bundles could also be prefetched at a lower priority so that subsequent user actions that would depend on asynchronously loaded code could be ready and potentially even parsed in the background ahead of when they are needed.
Parcel has a graph of all of the resources in an application at build time, so it is the perfect tool to automatically insert these <link>
tags.
Preloading
<link rel="preload">
can be used to speed up page loads by hoisting downloads of resources that would normally be discovered after loading something else. For example, the browser normally needs to download and parse CSS in order to discover background images and fonts. By hoisting these into the HTML, the browser can download those resources in parallel instead. This could be done automatically by Parcel at build time.
In the HTML packager, Parcel already inserts <script>
and <link rel="stylesheet">
tags for each of the directly referenced bundles. We should also insert <link rel="preload">
tags for each resource that is referenced by these bundles that would be immediately loaded, e.g. fonts, images, etc.
In the JS runtime, we should do the same thing. We already load CSS in parallel with JS when loading a code split bundle, but we should also load images and other resources referenced in that CSS/JS. This can be done by dynamically inserting <link rel="preload">
tags into the HTML at runtime.
This will require some bundling changes. Currently, those resources are marked as “async” so are placed in their own bundle group. Ideally, they would be placed in the same bundle group as the bundle that referenced them. This way, the JS runtime and HTML packager can pick up those resources and preload them the same way they already handle JS and CSS.
We could also handle responsive preloading by automatically adding the media
attribute to the <link>
tag based on the @media
rule a resource was nested inside of in CSS. This would avoid preloading media that would not be used on the user’s device.
One concern is how to detect whether resources are used immediately or not. For example, a CSS class with a background image may not be applied immediately. Chrome logs warnings in the console when a preloaded resource isn’t used within a few seconds of page load. It could be argued that loading CSS that isn’t used right away is also bad, but either way, there’s no way to know at build time whether a resource will be used immediately. Perhaps these should be prefetches rather than preloads? Or maybe we need a syntax in CSS/JS to specify which resources should be preloaded and which should be prefetched? Feedback and ideas wanted here.
Prefetching
<link rel="prefetch">
can be used to speed up subsequent navigations within an application by prefetching resources in the background while the browser is idle. This means that those downloads won’t block higher priority resources such as those for the current page, but hopefully by the time the user navigates to a subsequent part of the app many resources are already loaded. These <link>
tags can be embedded into the HTML, or added by JavaScript on demand.
Parcel should allow users and frameworks to trigger prefetching of the resources that would be loaded for a code split bundle. Dynamic import()
normally downloads and immediately executes a bundle and it’s dependencies, but prefetching would only download and cache the resource without immediately executing it.
One way to do this is to support a runtime API to trigger prefetching. Parcel has all of the metadata about what bundles to load already, so we just need to expose this information. For example, on mouse over on a button, prefetching could be triggered so that the browser has a head start when the user decides to click it.
import prefetch from '@parcel/prefetch';
async function onMouseOver() {
prefetch('./other');
}
async function onClick() {
await import('./other');
}
This could be statically analyzed at build time to generate the necessary prefetching code.
Feedback
Please leave feedback in the comments below. It would be really useful to hear your usecases and ideas for this.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:11
- Comments:13 (6 by maintainers)
I think it would be nice to have magic comments like in Webpack, for the sake of simplicity of migration from one solution to another.
static prefetching and preloading is implemented. We should actually document this
https://github.com/parcel-bundler/parcel/blob/v2/packages/core/integration-tests/test/integration/dynamic-static-prefetch/async.js
https://github.com/parcel-bundler/parcel/blob/v2/packages/core/integration-tests/test/integration/dynamic-static-preload/async.js
https://github.com/parcel-bundler/parcel/pull/5158