Use Preload&Prefetch to optimize the resource loading of the front-end page

For front-end pages, the loading of static resources plays a vital role in page performance. This article will introduce the two resource instructions provided by the browser-preload/prefetch, which can assist the browser in optimizing the order and timing of resource loading and improve page performance.

1. Start with an example

Use Preload&Prefetch to optimize the resource loading of the front-end page

As shown in the figure above, we have developed a simple cashier counter. During the payment process, you can expand the coupon list to select the corresponding coupon. As you can see from the animation, when the list is first expanded, the background of the coupon is gradually displayed, and the experience is not very good.

The cause of the problem is also obvious. Because the background uses a specially designed visual image, the image needs to be loaded when the coupon list is expanded. The process of the background gradually showing is actually the process of loading the image; when the Internet speed is slow, this problem will more obvious. So, how to solve this problem?

If we analyze it carefully, we will find that the cause of the problem is that the background image is loaded too late.

If the background image can be loaded before the coupon list is rendered, this problem will not occur. Starting from this idea, we may think of the following two solutions:

  1. Use inline images, that is, convert images to base64 encoded data-url. This method actually integrates the image information into the css file, avoiding the separate loading of image resources. But the image inlining will increase the size of the css file and increase the rendering time of the first screen.
  2. Use js code to preload pictures
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];
    }
}

This solution mainly uses the browser's caching mechanism. The js code loads the corresponding image in advance at a specific time, and the coupon list can be directly obtained from the cache when it is rendered. However, this solution adds additional code, requires you to control the loading timing yourself, and hard-code the url of the image in the logic. 

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

So, is there a better solution? The answer is prefetch-a pre-loading scheme natively provided by the browser.

2. What is prefetch?

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

Specifically, the browser implements preloading through the <link rel="prefetch" href="/library.js"> tag.

Among them, rel="prefetch" is called Resource-Hints (resource hints), which is an instruction to assist the browser in resource optimization.

Similar directives include rel="preload", which we will mention later.

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

Check the current loading effect of the coupon list.

Use Preload&Prefetch to optimize the resource loading of the front-end page

Sure enough, we successfully achieved the desired effect. So how does the browser do it? Let's open Chrome's Network panel to find out:

Use Preload&Prefetch to optimize the resource loading of the front-end page

Use Preload&Prefetch to optimize the resource loading of the front-end page

As you can see, the loading request of the coupon background image ticket_bg.png has appeared in the request list on the first screen. The request itself looks no different from a normal request; after expanding the coupon list, a new ticket_bg is added to the network. For the png access request, we quickly discovered that although the status of this request is also 200, it has a special mark—prefetch cache, indicating that the requested resource comes from the prefetch cache. This performance verifies the definition of prefetch above, that is, the browser pre-loads resources in idle time, and quickly obtains them directly from the browser cache when they are actually used.

Three, Preload

From the above case, we have realized the powerful ability of the browser to preload resources. In fact, preloading is a broad concept, prefetch is only one of the specific implementation methods, in this section we introduce another preloading method preload. As we mentioned above, preload and prefetch belong to the Resource-Hints of the browser and are used to assist the browser in resource optimization. In order to distinguish between the two, prefetch is usually translated as prefetching, and preload is translated as preloading.

The attribute value preload of the rel attribute of the element allows you to write some declarative resource acquisition requests inside the element in your HTML page, which can specify which resources are needed immediately after the page is loaded. For such resources that are needed immediately, you may want to start acquiring them in the early stages of the page loading life cycle, and preload them before the browser's main rendering mechanism intervenes. This mechanism enables resources to be loaded and available earlier, and is less likely to block the initial rendering of the page, thereby improving performance.

Simply put, it is to explicitly declare a high-priority resource through the <link rel="preload" href="xxx" as="xx"> tag to force the browser to request the resource in advance without blocking the normal onload of the document. We also use a practical case for detailed introduction.

Use Preload&Prefetch to optimize the resource loading of the front-end page

The above picture is another cash register we developed. For localization considerations, a custom font is used in the design. After the development is completed, we found that the text will have a short font style flash (FOUT, Flash of Unstyled Text) when the page is first loaded, which is more obvious when the network situation is poor (as shown in the animation). The reason is that the font file is imported by css and will be loaded after the css is parsed. The browser can only use the degraded font before the loading is complete. In other words, the time to load the font file is too late, and you need to tell the browser to load it in advance, which is exactly where preload comes in.

We add the preload tag to the head of the entry html file:

<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>

Look again at the effect of the first loading of the page:

Use Preload&Prefetch to optimize the resource loading of the front-end page

The flashing of font style is gone! Let's compare the network panel before and after using preload.

before use:

Use Preload&Prefetch to optimize the resource loading of the front-end page

After use:

Use Preload&Prefetch to optimize the resource loading of the front-end page

It can be found that the loading time of the font file has been significantly advanced, and it is loaded soon after the browser receives the html.

Note: Preload link must set the as attribute to declare the type of resource (font/image/style/script, etc.), otherwise the browser may not be able to load the resource correctly.

Fourth, the specific practice of Preload and Prefetch

1、**preload-webpack-plugin**

The two examples we gave in the previous article were to manually add relevant code to the entry html:

<head>
    ...
    <link rel="prefetch" href="static/img/ticket_bg.a5bb7c33.png">
    ...
</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>

This is obviously not convenient enough, and the resource path is hard-coded in the page (in fact, the hash in the ticket_bg.a5bb7c33.png suffix is ​​automatically generated during the construction process, so the hard-coding method itself does not work in many scenarios). The webpack plugin preload-webpack-plugin can help us automate the process, combined with htmlWebpackPlugin to insert the link tag during the build process.

const PreloadWebpackPlugin = require('preload-webpack-plugin');
...
plugins: [
  new PreloadWebpackPlugin({
    rel: 'preload',
    as(entry) {  //资源类型
      if (/\.css$/.test(entry)) return 'style';
      if (/\.woff$/.test(entry)) return 'font';
      if (/\.png$/.test(entry)) return 'image';
      return 'script';
    },
    include: 'asyncChunks', // preload模块范围,还可取值'initial'|'allChunks'|'allAssets',
    fileBlacklist: [/\.svg/] // 资源黑名单
    fileWhitelist: [/\.script/] // 资源白名单
  })
]

PreloadWebpackPlugin configuration is relatively simple in general, and the include attribute needs to be paid attention to. The default value of this attribute is'asyncChunks', which means that only asynchronous js modules are preloaded; if you need to preload resources such as images and fonts, you need to set it to'allAssets', which means to process all types of resources.

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

For asynchronously loaded modules, you can also use the built-in / webpackPreload: true / mark of webpack for more fine-grained control.

Take the following code as an example, webpack will generate the <link rel="preload" href="chunk-xxx.js" as="script"> tag and add it to the head of the html page.

import(/* webpackPreload: true */ 'AsyncModule');

Note: The configuration of prefetch is similar to preload, but there is no need to set the as attribute.

2. Usage scenarios

From the previous introduction, we can see that the original intention of preload is to load the key resources required by the first screen as soon as possible, thereby improving page rendering performance.

At present, browsers basically have predictive parsing capabilities, which can parse resources in and out of the entry html in advance, so entry script files, style files, etc. do not need to be preloaded.

However, some resources hidden in CSS and JavaScript, such as font files, are themselves key resources on the first screen, but they will be loaded by the browser after the CSS file is parsed. This kind of scene is suitable for using preload to declare and load resources as soon as possible to avoid page rendering delay. 

Unlike preload, prefetch declares resources that may be accessed in the future, so it is suitable for resource caching of asynchronously loaded modules and other routing pages that may jump to; for some resources that are likely to be accessed in the future, such as the coupon in the above case The background image of the list, common loading failure icons, etc. are also more applicable.

3. Best practices

Based on the above sharing of usage scenarios, we can summarize a more general best practice:

  • No need to use preload specially in most scenarios
  • Similar to font files, which are key resources on the first screen hidden in scripts and styles, it is recommended to use preload
  • Asynchronously loaded modules (typically such as the non-home page in a single-page system) prefetch is recommended
  • Resources with a high probability of being accessed can use prefetch to improve performance and experience

4. ** Default configuration of vue-cli3**

  • preload

By default, a Vue CLI application will automatically generate preload prompts for all files needed for initial rendering. These tips will be injected by @vue/preload-webpack-plugin, and can be modified and deleted through chainWebpack's config.plugin('preload').

  • prefetch

By default, a Vue CLI application will automatically generate prefetch prompts for all JavaScript files generated as async chunks (the product of dynamic import() on-demand code splitting). These tips will be injected by @vue/preload-webpack-plugin, and can be modified and deleted through chainWebpack's config.plugin('prefetch').

Five, summary and stepping on the pit

1. The essence of preload and prefetch are preloading, that is, load first, then execute, and load and execute are decoupled.

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

3. Preload is used to declare the key resources of the current page to force the browser to load as soon as possible; and prefetch is used to declare resources that may be used in the future and load them when the browser is idle.

4. Do not abuse preload and prefetch, they need to be used in appropriate scenarios.

5. The crossorigin attribute of preload font resources must be set, otherwise it will cause repeated loading. 

The reason is that if the crossorigin attribute is not specified (even if it is of the same origin), the browser will use the anonymous mode CORS to preload, so that the two requests cannot share the cache.

6. The caching of preload and prefetch resources is described in an article by Google developers: if the resource can be cached (for example, there is a valid cache-control and max-age), it is stored in the HTTP cache (That is, the disk cache) can be used by current or future tasks; if the resource cannot be cached in the HTTP cache, instead, it is placed in the memory cache until it is used. 

However, we tested it in the Chrome browser (version 80), and the result was not the case. Set the server's cache policy to no-store, and observe the resource loading.

Use Preload&Prefetch to optimize the resource loading of the front-end page

It can be found that the second load of ticket_bg.png is not obtained from the local cache, and it is still loaded from the server. Therefore, if you want to use prefetch, the corresponding resources must be properly cached.

7. Sites without a valid https certificate cannot use prefetch, and pre-fetched resources will not be cached (discovered during actual use, the reason is unknown).

8. Finally, let's look at the browser compatibility of preload and prefetch.

Use Preload&Prefetch to optimize the resource loading of the front-end page

Use Preload&Prefetch to optimize the resource loading of the front-end page

It can be seen that the compatibility of the two is not very good at present. Fortunately, browsers that do not support preload and prefetch will automatically ignore it, so they can be used as a progressive enhancement function to optimize the resource loading of our pages and improve performance and user experience.

Author: Sha Chaoheng

Guess you like

Origin blog.51cto.com/14291117/2549190