This article will systematically introduce Webpack4 resources inlining (HTML/CSS/JS/Image/Font) the correct posture

First, let’s look at what resource inlining is.

What is resource inlining?

An inline resource is a method of inserting a resource into another resource inline. Let’s take a look at some examples.

HTML inline CSS, this is actually what we call inline CSS or inline CSS. We can write a few lines of reset CSS and embed them in the HTML with the style tag:

<! DOCTYPE html> <html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        body {
            font-size: 12px;
            font-family: Arial, Helvetica, sans-serif;
            background: #fff;
        }
        ul, ol, li {
            list-style-type: none;
        }
    </style>
</head>
<body>
    
</body>
</html>
Copy the code

CSS inline images are small images that are embedded in CSS using base64. We can inline the search small icon into CSS:

// index.css .search { background: url(data:image/png; base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABJ0lEQVQ4T6XSsUoEMRAG4H/ClZaLmbSW1pZ6+gAnFrK+gZXoK6jvIILgE6gIcnYWg mJno6AgYp1Z2EcIGQnsHbuaQ9abMkO+TGaGMGfQnPfxC3DOrajqPoB1AArgnohOvffPucc6ADMfAjgCUMYYH9MFY8wagEsAxyKScp2YAtbaERGNRST7LWZWV d2squq2LbSBMyK6E5GrXKnW2i1jzMh7v5sFmPkzhDCs69rngKIo3GAweBKRpVnAVwhh9Q/gRUQWs4Bz7jzGeFNV1ThXATOXAA5EJDV1Gr2aSETb3vvrLJAOm TmNKY2yVNUHVSVjzBDABYA3ADsi8j4TSIlmkfYAbABYUNUPACdE9NpAHaTXKjPz8k+kF9B8s4P0BibIpBf/AtpN/AYx54AR58WxmQAAAABJRU5ErkJggg==)  no-repeat; }Copy the code

Now that you know the basic concept of resource inlining, you may ask what does resource inlining mean? Let’s look at several dimensions of why we need resources to be inlined.

Meaning of resource inlining

The meaning of resource inlining is explained from three aspects: engineering maintenance, page loading performance, and page loading experience.

Engineering maintenance

Let’s look at what resource inlining means for project maintenance. This is a basic HTML structure. In today’s popular Hybrid development architecture, there will be a H5 page, corresponding to the multi-page application (MPA) in front-end engineering.

We use the HTML-webpack-plugin to package multi-page applications, with an HTML template for each page. Each HTML template contains a lot of similar content, such as meta information, or placeholders for SSR, etc. Imagine the impact on code maintenance if you copied the meta code below and put it in each HTML template.

<meta charset="UTF-8">
<meta name="viewport" content="viewport-fit=cover,width=device-width,initial-scale=1,user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="keywords" content="Now,now live, live, Tencent live,QQ live, beauty live, nearby live, talent live, small video, personal live, beauty video, online live, mobile live">
<meta name="name" itemprop="name" content="NOW Live -- Tencent's national video social Live Broadcast platform"><meta name="description" itemprop="description" content="Live NOW, tencent's universal high definition video broadcast platform, Chinese and foreign great coffee, the most in web celebrity, grassroots idols, the star artist, beauty, fresh meat, stir hand made jokes, all kinds of food, music, travel, fashion, fitness guru interactive broadcast 24 hours a day with you, all kinds of wonderful work exciting live play, let you try, you will find that, Turns out anyone can make money as an anchor!">
<meta name="image" itemprop="image" content="https://pub.idqqimg.com/pc/misc/files/20170831/60b60446e34b40b98fa26afcc62a5f74.jpg"><meta name="baidu-site-verification" content="G4ovcyX25V">
<meta name="apple-mobile-web-app-capable" content="no">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<link rel="dns-prefetch" href="//11.url.cn/">
<link rel="dns-prefetch" href="//open.mobile.qq.com/">
Copy the code

The recommended thing to do at this point is to maintain a meta-html and put the above code content in it. Each HTML template inlines the meta. HTML fragment.

Another common scenario of engineering maintenance is the inlining of pictures, fonts and other files. For example, many students usually go to the Internet to find an online Base64 encoding tool (e.g. www.base64code.com/) to encode various images (PNG, JPG, GIF) or fonts (TTF, OTF), and then place the encoded long string into the code. The search icon above, for example, is a long string that has no meaning in the source code and is a disaster for maintainers.

// index.css .search { background: url(data:image/png; base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABJ0lEQVQ4T6XSsUoEMRAG4H/ClZaLmbSW1pZ6+gAnFrK+gZXoK6jvIILgE6gIcnYWg mJno6AgYp1Z2EcIGQnsHbuaQ9abMkO+TGaGMGfQnPfxC3DOrajqPoB1AArgnohOvffPucc6ADMfAjgCUMYYH9MFY8wagEsAxyKScp2YAtbaERGNRST7LWZWV d2squq2LbSBMyK6E5GrXKnW2i1jzMh7v5sFmPkzhDCs69rngKIo3GAweBKRpVnAVwhh9Q/gRUQWs4Bz7jzGeFNV1ThXATOXAA5EJDV1Gr2aSETb3vvrLJAOm TmNKY2yVNUHVSVjzBDABYA3ADsi8j4TSIlmkfYAbABYUNUPACdE9NpAHaTXKjPz8k+kF9B8s4P0BibIpBf/AtpN/AYx54AR58WxmQAAAABJRU5ErkJggg==)  no-repeat; }Copy the code

We can avoid this problem by using a more elegant inline resource syntax, as described later in this article.

Page loading performance

The second point of inlining resources is to reduce the number of HTTP requests, although it may not make as much sense if your site uses HTTP2. Base64 of various small images and fonts (e.g., less than 5K) in the production environment can greatly reduce the number of page requests, thus improving page load times.

Page loading experience

Another important aspect of resource inlining is to improve the page loading experience. We all know that browsers parse HTML source from top to bottom, so we put CSS at the top and JS at the bottom. Take the SSR scenario as an example. If the packed CSS is not inlined into THE HTML, the HTML comes out with the page structure already in place, but you still need to send a request to request the CSS. At this time, the page flicker will appear, which is more obvious when the network condition is poor.

Type of resource inline

Resource inline types include:

  • HTML inline
  • CSS inline
  • JS inline
  • Images and fonts are inline

If you’ve ever used FIS or looked at FIS documentation, you’ll see that FIS support for resource inlining is great. Detailed documentation: Embedded resources

FIS HTML Inline HTML fragment:

 <link rel="import" href="demo.html? __inline">
Copy the code

FIS HTML inline JS script:

  <script type="text/javascript" src="demo.js? __inline"></script>
Copy the code

Next, let’s look at the implementation of each type of inlining in webpack4.

HTML inline

Basic version

The idea of inlining HTML fragments, CSS, or JS(compiled by Babel, such as inlining an NPM component) is simply to read the contents of a file and insert them in place. We can use [email protected], the latest raw-loader has problems (because it exports modules using export Default), but you can implement such a raw-loader yourself.

Version 0.5.1 raw-loader code:

module.exports = function(content) {
	this.cacheable && this.cacheable();
	this.value = content;
	return "module.exports = " + JSON.stringify(content);
}
Copy the code

The inline syntax implemented with raw-Loader is as follows:

// Inline HTML fragments${ require('raw-loader! ./meta.html')}// inline JS <script>${ require('raw-loader! babel-loader! . /.. /node_modules/lib-flexible/flexible.js')}</script>
Copy the code

Enhanced version

Can we implement a more developer-friendly syntax candy, such as implementing a loader to parse HTML? __inline syntax. Here I implement an HTML-inline-loader that looks like this:

const fs = require('fs');
const path = require('path');

const getContent = (matched, reg, resourcePath) => {
    const result = matched.match(reg);
    const relativePath = result && result[1];
    const absolutePath = path.join(path.dirname(resourcePath), relativePath);
    return fs.readFileSync(absolutePath, 'utf-8');
};

module.exports = function(content) { const htmlReg = /<link.*? href=". *? \__inline">/gmi; const jsReg = /<script.*? src=". *? \? __inline". *? >. *? <\/script>/gmi; content = content.replace(jsReg, (matched) => { const jsContent = getContent(matched, /src=\ "(. *)? __inline/, this.resourcePath); return `text/javascript">${jsContent}</script>`;
  }).replace(htmlReg, (matched) => {
    const htmlContent = getContent(matched, /href="(. *) \? __inline/, this.resourcePath);return htmlContent;
  });

  return `module.exports = ${JSON.stringify(content)}`;
}
Copy the code

Then, you can use it like this:

<! DOCTYPE html> <html lang="en">
<head>
    <link href="./meta.html? __inline">
    <title>Document</title>
    <script type="text/javascript" src=".. /.. /node_modules/lib-flexible/flexible.js? __inline"></script>
</head>
<body>
    <div id="root"> <! --HTML_PLACEHOLDER--></div> <! --INITIAL_DATA_PLACEHOLDER--> </body> </html>Copy the code

View the effect:

CSS inline

Usually, for a better loading experience, we will inline the packed CSS to the HTML header, so that the CSS can be rendered directly after the HTML is loaded and the page does not flash. So how does CSS inline work?

The idea behind CSS inlining is to extract all the CSS generated during the page packaging process into a single file, and then inline the CSS file in the HTML head. The mini-CSS-extract-plugin and htML-inline-CSs-webpack-plugin are needed to implement the inline function of CSS.

// webpack.config.js

const path = require('path');

module.exports = {
    entry: {
        index: './src/index.js',
        search: './src/search.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]_[chunkhash:8].js'
    },
    mode: 'production',
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name]_[contenthash:8].css'
        }),
        new HtmlWebpackPlugin(),
        new HTMLInlineCSSWebpackPlugin()
    ]
};

Copy the code

Note: html-inline-CSS-webpack-plugin needs to be placed after htMl-webpack-plugin.

Images and fonts are inline

Basic version

Inlining images and fonts can be done with url-loader, for example you can modify the Webpack configuration to automatically base64 images or font files smaller than 10K during the build phase.

// webpack.config.js

const path = require('path');

module.exports = {
    entry: {
        index: './src/index.js',
        search: './src/search.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]_[chunkhash:8].js'
    },
    mode: 'production',
    module: {
        rules: [
            {
                test: /.(png|jpg|gif|jpeg)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            name: '[name]_[hash:8].[ext]'.limit}}]}, {test: /.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            name: '[name]_[hash:8][ext]'.limit: 10240}}]}};Copy the code

Enhanced version

However, the biggest drawback of URL-loader is that it can’t set the automatic encoding of a certain image. To solve this problem, we can learn from the syntax of FIS. An __inline syntax sugar that automatically base64 encodes a reference to an image when the suffix is seen. This feature is also easy to implement. Please refer to my implementation of inline-file-loader.

export default function loader(content) {
  const options = loaderUtils.getOptions(this) || {};

  validateOptions(schema, options, {
    name: 'File Loader',
    baseDataPath: 'options'}); const hasInlineFlag = /\? __inline$/.test(this.resource);if (hasInlineFlag) {
    const file = this.resourcePath;
    // Get MIME type
    const mimetype = options.mimetype || mime.getType(file);

    if (typeof content === 'string') {
      content = Buffer.from(content);
    }

    return `module.exports = ${JSON.stringify(
      `data:${mimetype || ''}; base64,${content.toString('base64')}`
    )}`;
  }
Copy the code

With the inline function of the image, we can change the previous inlining of the search icon icon to:

// index.css .search { background: url(./search-icon.png? __inline) no-repeat; }Copy the code

The last

Here’s a code demo for this article, if you need it.

  • The inline – resource demo presentation
  • The inline HTML – loader source code
  • The inline – file – loader source code

My personal blog: github.com/cpselvis/bl…