For front-end pages, static resource loading plays an important role in page performance. This article introduces two resource commands provided by the browser, preload/prefetch, which can help the browser optimize the order and timing of resource loading to improve page performance.

Start with an example

As shown in the picture above, we developed a simple cashier, and during the payment process, you can expand the coupon list to select the corresponding coupons. As can be seen from the driven graph, when the list is expanded for the first time, the coupon background is gradually displayed, which is not a good experience.

The reason for the problem is also obvious. Because the background uses a visually designed image, the coupon list needs to be loaded when expanding, and the background fading process is actually the image loading process. The problem is even more pronounced when the Internet is slow. So, how to solve this problem?

A closer look reveals that the problem is that the background image was loaded too late.

This problem will not occur if the background image is loaded before the coupon list is rendered. From this point of view, we may think of the following two schemes:

  1. Use an inline image, which translates the image into a Base64-encoded data-URL. In this way, the information of the image is integrated into the CSS file, and the image resources are not loaded separately. But inlining images increases the size of CSS files and the time it takes to render the first screen.
  2. Preloading images using JS code
preloadImage() { const imgList = [ require('@/assets/imgs/error.png'), require('@/assets/imgs/ticket_bg.png') ]; for (let i = 0; i < imgList.length; i++) { const newIMG = new Image(); newIMG.src = imgList[i]; }}Copy the code

This scheme mainly makes use of the browser’s cache mechanism. The JS code loads corresponding pictures in advance at a specific time, and the coupon list can be directly obtained from the cache when rendering. However, this solution adds extra code that requires you to control the loading time and hardcode the image URL into the logic.

It can be seen that the above two schemes can solve our problems, but both have some disadvantages.

So, is there a better solution? The answer is Prefetch – a preloading scheme provided natively by the browser.

What is prefetch?

Prefetch (link prefetch) is a browser mechanism that uses idle browser time to download or prefetch documents that a user may access in the near future. The Web page provides a set of prefetch prompts to the browser and silently starts pulling the specified document and storing it in the cache after the browser finishes loading the current page. When the user accesses one of the prefetched documents, it can be quickly retrieved from the browser cache. –MDN

Specifically, the browser implements preloading through tags.

Rel =”prefetch” is called resource-hints, or instructions to help the browser optimize resources.

A similar instruction is rel=”preload”, which we’ll talk about later.

<head>
    ...
    <link rel="prefetch" href="static/img/ticket_bg.a5bb7c33.png">
    ...
</head>
Copy the code

See how the coupon list now loads.

Sure enough, we succeeded in achieving the desired effect. So how does the browser do this? Let’s open Chrome’s Network panel to find out:

As you can see, the coupon background ticket_bg.png loading request has appeared in the first screen request list. The request itself looks no different from ordinary requests. After expanding the coupon list, a new ticket_bg.png access request was added to the network. We soon found that although the status of this request was also 200, it had a special marker — Prefetch Cache, indicating that the resources for this request came from the Prefetch cache. This representation validates the definition of Prefetch above, where the browser preloads resources at idle time and quickly retrieves them directly from the browser cache when they are actually used.

Three, the Preload

From the above example, we can see the powerful ability of browsers to preload resources. In fact, preloading is a broad concept, and Prefetch is only one of the concrete implementation methods. In this section, we will introduce another preload method, Preload. As mentioned above, Both Preload and Prefetch belong to resource-Hints of the browser, which are used to assist the browser in Resource optimization. To distinguish between the two, prefetch is usually translated as prefetch and preload as preload.

The rel attribute value of the element preload allows you to write declarative resource requests inside elements of your HTML page that specify which resources are needed immediately after the page loads. For this immediate-need resource, you may want to acquire it early in the page loading lifecycle, preloading it before the browser’s main rendering mechanism kicks in. This mechanism allows resources to be loaded and available earlier and is less likely to block the initial rendering of the page, thus improving performance.

In simple terms, an explicit declaration of a high-priority resource via a tag forces the browser to request the resource in advance without blocking the document’s normal onload. We also use a practical case to elaborate.

The picture above is another cashier that we developed. For the sake of localization, custom fonts are used in the design. After development, we found that the Text would have a short FOUT (Flash of Unstyled Text) when the page was first loaded, which would be more pronounced in poor network conditions (as shown in the GIF). The reason for this is that the font files are imported from CSS and are not loaded until the CSS has parsed them. Browsers can only use degraded fonts until the loading is complete. That is, font files load too late and you need to tell the browser to load ahead of time, which is where Preload comes in.

We add the preload tag to the entry HTML file head:

<head>
    ...
    <link rel="preload" as="font" href="<%= require('/assets/fonts/AvenirNextLTPro-Demi.otf') %>" crossorigin>
    <link rel="preload" as="font" href="<%= require('/assets/fonts/AvenirNextLTPro-Regular.otf') %>" crossorigin>
    ...
</head>
Copy the code

Look again at the effect of the first page load:

Font style flicker no more! Let’s compare the Network panel before and after using Preload.

Before use:

After use:

You can see that the loading time of the font file is significantly earlier, loading soon after the browser receives the HTML.

Note: Preload Link must set the AS attribute to declare the type of the resource (font/image/style/script, etc.) or the browser may not load the resource correctly.

4. Specific practices of Preload and Prefetch

1, the preload – webpack – the plugin

In the previous two examples, we manually added code to the entry HTML:

<head>
    ...
    <link rel="prefetch" href="static/img/ticket_bg.a5bb7c33.png">
    ...
</head>
Copy the code

<head>
    ...
    <link rel="preload" as="font" href="<%= require('/assets/fonts/AvenirNextLTPro-Demi.otf') %>" crossorigin>
    <link rel="preload" as="font" href="<%= require('/assets/fonts/AvenirNextLTPro-Regular.otf') %>" crossorigin>
    ...
</head>
Copy the code

This is obviously inconvenient and hardcodes the resource path into the page (in fact, the hash in the ticket_bg.a5bb7c33.png suffix is automatically generated by the build process, so hardcoding itself won’t work in many scenarios). The Webpack plugin preload-webpack-Plugin helps automate this process by inserting link tags during the build process with htmlWebpackPlugin.

const PreloadWebpackPlugin = require('preload-webpack-plugin'); . plugins: [ new PreloadWebpackPlugin({ rel: 'our preload, As (entry) {// Resource type if (/\.css$/.test(entry)) return 'style'; if (/\.woff$/.test(entry)) return 'font'; if (/\.png$/.test(entry)) return 'image'; return 'script'; }, include: 'asyncChunks', / / preload module range, but also the values' initial' | 'allChunks' |' allAssets, fileBlacklist: / / \. SVG / / / resources blacklist fileWhitelist: [/\.script/] // Resource whitelist})]Copy the code

The PreloadWebpackPlugin configuration is generally simple, with the include attribute in mind. The default value of this property is ‘asyncChunks’, indicating that only asynchronous JS modules are preloaded. If you need to preload resources such as images and fonts, you need to set them to ‘allAssets’, indicating that all types of resources are processed.

But in general, we don’t want to expand the preloading range too much, so we need to control it through fileBlacklist or fileWhitelist.

For asynchronously loaded modules, there is also more fine-grained control via the /_ webpackPreload: true _/ flag built into Webpack.

For example, webPack generates tags to add to the header of an HTML page.

import(/* webpackPreload: true */ 'AsyncModule');
Copy the code

Note: The configuration of prefetch is similar to that of preload, but you do not need to set the AS attribute.

2. Application scenarios

As you can see from the previous section, Preload is designed to load critical resources for the first screen as early as possible to improve page rendering performance.

At present, browsers basically have predictive parsing ability, which can parse the resources of the entry HTML chain in advance, so the entry script files, style files and so on do not need to preload deliberately.

However, some resources hidden in CSS and JavaScript, such as font files, are themselves key resources on the first screen, but will only be loaded by the browser after the CSS file is parsed. This scenario is suitable for declarations using preload to load resources early and avoid page rendering delays.

Different from Preload, Prefetch declares resources that may be accessed in the future. Therefore, prefetch is suitable for caching resources for modules that are loaded asynchronously and other routing pages that may be jumped to. It is also suitable for some resources that are likely to be accessed in the future, such as the background image of the coupon list in the case above and the common loading failure icon.

3. Best practices

Based on the usage scenarios shared above, we can conclude a common best practice:

  • Preload is not required for most scenarios
  • Preload is recommended for key first-screen resources such as font files hidden in scripts and styles
  • Modules loaded asynchronously (typically non-home pages in single-page systems) are recommended to use Prefetch
  • Prefetch can be used to improve performance and experience for resources that have a high probability of being accessed

4. The default configuration of VUE-CLI3

  • preload

By default, a Vue CLI application automatically generates preload prompts for all files required for initial rendering. These hints are injected by @vue/preload-webpack-plugin and can be modified and removed via the config.plugin(‘preload’) of chainWebpack.

  • prefetch

By default, a Vue CLI application automatically generates prefetch prompts for all JavaScript files generated as Async Chunks (a product of code splitting on demand via dynamic import()). These hints are injected by @vue/preload-webpack-plugin and can be modified and removed via the config.plugin(‘prefetch’) of chainWebpack.

Five, summary and trample pit

1. Both Preload and Prefetch are preloading in nature, that is, loading first and executing later. Loading is decoupled from execution.

2. Preload and prefetch do not block onload of the page.

3, preload is used to declare the key resources of the current page, forcing the browser to load as soon as possible; Prefetch is used to declare resources that may be used in the future and load them when the browser is idle.

4. Don’t abuse preload and prefetch, use them in the right context.

5, Preload font resources must be set to crossorigin property, otherwise it will cause repeated loading.

The reason is that if the Crossorigin attribute is not specified (even if it is the same origin), the browser will preload the CORS in anonymous mode, so that the cache cannot be shared between the two requests.

6. Caching of preload and Prefetch resources is explained in an article by Google developers: If the resource can be cached (e.g., with valid cache-control and max-age), it is stored in the HTTP cache (disk cache) and can be used by current or future tasks. If the resource cannot be cached in the HTTP cache, it is instead placed in the in-memory cache until it is used.

However, when we tested Chrome (version 80), this was not the case. Set the cache policy of the server to no-store, and check whether resources are loaded.

You can see that the second load of ticket_bg.png is not obtained from the local cache, but is still loaded from the server. Therefore, if Prefetch is to be used, the corresponding resources must be properly cached.

7. Sites without valid HTTPS certificates cannot use Prefetch, and the pre-fetched resources will not be cached (discovered during actual use, for unknown reasons).

8. Finally, let’s look at browser compatibility between Preload and Prefetch.

As you can see, the compatibility of both is not very good at present. Fortunately, browsers that don’t support Preload and Prefetch automatically ignore them, so they can be used as incremental enhancements to optimize resource loading on our pages, improving performance and user experience.

Sha Chaoheng