使用HTML Bundler插件进行WebPack生成HTML文件
#javascript #html #网络开发人员 #webpack

WebPack用于构建JavaScript捆绑包,但不支持HTML或CSS文件开箱即用。
要将JavaScript和CSS与HTML捆绑在一起,我们可以使用功能强大的html-bundler-webpack-plugin

安装

第一步是安装插件:

npm install html-bundler-webpack-plugin --save-dev

安装样式的其他软件包:

npm install css-loader sass-loader sass --save-dev

一个简单的静态网站

我们创建了一些静态文件,以说明插件如何处理HTML中的引用到脚本,样式和图像源文件:

src/js/main.js
src/scss/styles.scss
src/views/index.html
src/images/favicon.ico
src/images/picture.png (size > 2 KB)
src/images/icon.png    (size < 2 KB)

src/view/目录中创建index.html文件:

<!doctype html>
<html lang="en">
<head>
  <title>Home</title>
  <link href="../images/favicon.ico" rel="shortcut icon">
  <link href="../scss/styles.scss" rel="stylesheet">
  <script src="../js/main.js" defer="defer"></script>
</head>
<body>
  <h1>Homepage</h1>
  <img src="../images/picture.png" alt="picture">
  <img src="../images/icon.png" alt="icon">
</body>
</html>

要将我们的样式和脚本文件绑定到HTML,我们使用源文件的相对路径直接在HTML中指定它们。

具有复杂的文件结构,某些相对路径可能就像../../../images。为了避免这种不可读的路径,建议使用WebPack别名。

该插件允许我们同时使用相对路径和WebPack别名。

我们将使用别名,因此我们将在WebPack resolve.alias选项中定义它们:

{
  '@scripts': path.join(__dirname, 'src/js'),
  '@styles': path.join(__dirname, 'src/scss'),
  '@images': path.join(__dirname, 'src/images'),
}

然后将index.html中的所有相对路径更改为别名:

<!doctype html>
<html lang="en">
<head>
  <title>Hello World</title>
  <link href="@images/favicon.ico" rel="shortcut icon">
  <link href="@styles/styles.scss" rel="stylesheet">
  <script src="@scripts/main.js" defer="defer"></script>
</head>
<body>
  <h1>Hello World!</h1>
  <img src="@images/picture.png" alt="picture">
  <img src="@images/icon.png" alt="icon">
</body>
</html>

所以HTML看起来更好,更干净。

处理HTML模板

WebPack不知道如何处理HTML模板,但是html-bundler-webpack-plugin可以处理模板并将生成的HTML放入输出目录中。

配置webpack.config.js中的插件以处理index.html

const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = {
  output: {
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },

  plugins: [
    new HtmlBundlerPlugin({
      entry: {
        // define templates here
        index: 'src/views/index.html',
      },
    }),
  ],
  // ...
};

我们可以在插件的entry选项中定义HTML模板。
entry键是没有.html扩展名的输出文件名,该值是模板文件。因此我们可以在entry选项中定义许多模板。

自动处理许多HTML模板

有一个HTML文件,但是在每次添加或删除文件时更新WebPack配置会很乏味。我们没有手动添加其他文件,让我们修改配置以自动包含它找到的所有HTML文件。

module.exports = {
  // ...
  plugins: [
    new HtmlBundlerPlugin({
      // relative or absolute path to templates
      entry: 'src/views/',
    }),
  ],
  // ...
};

如果entry选项是路径,则插件会自动找到所有模板,并将相同的目录结构保留在输出目录中。因此,我们可以在输入目录中创建许多模板,而无需重新启动以serve模式运行的WebPack。

处理样式

要编译HTML中指定的SASS文件到CSS,请添加模块规则:

module.exports = {
   // ...
   module: {
    rules: [
      {
        test: /\.(scss)$/,
        use: ['css-loader', 'sass-loader'],
      },
    ],
  },  
  // ...
};

插件从编译的SASS文件中提取CSS,并将作为单独文件保存到输出目录中。

处理图像

插件解析了HTML中指定的图像引用,并允许WebPack将这些图像复制到输出目录。我们可以使用规则中的generator.filename来定义图像的哈希输出文件名:

module.exports = {
   // ...
   module: {
    rules: [
      {
        test: /\.(ico|png|jp?g|svg)$/,
        type: 'asset/resource',
        generator: {
          filename: 'img/[name].[hash:8][ext]',
        },
      },
    ],
  },  
  // ...
};

要优化小图像的加载,我们可以使用规则中的type: 'asset'parser.dataUrlCondition.maxSize直接在HTML中插入图像:

module.exports = {
   // ...
   module: {
    rules: [
      {
        test: /\.(ico|png|jp?g|svg)$/,
        type: 'asset',
        generator: {
          // save images to file
          filename: 'img/[name].[hash:8][ext]',
        },
        parser: {
          dataUrlCondition: {
            // inline images < 2 KB
            maxSize: 2 * 1024
          }
        },
      },
    ],
  },  
  // ...
};

插件会自动内联图像较小的maxSize

JS和CSS的输出文件名

默认情况下,处理后的JS和CSS文件将在未解决的文件名下保存在输出目录中。但是建议使用哈希输出文件名。

我们可以在插件选项中定义JS和CSS的输出文件名:

module.exports = {
  // ...
  plugins: [
    new HtmlBundlerPlugin({
      entry: {
        index: 'src/views/index.html',
      },
      js: {
        // output filename for JS
        filename: 'js/[name].[contenthash:8].js',
      },
      css: {
        // output filename for CSS
        filename: 'css/[name].[contenthash:8].css',
      },
    }),
  ],
  // ...
};

可选,您可以使用js.inlinecss.inline插件选项中的HTML中的JS和CSS。

现场重新加载

要在更改后启用浏览器的重新加载,请将devServer选项添加到WebPack配置:

module.exports = {
  // enable HMR with live reload
  devServer: {
    static: path.join(__dirname, 'dist'),
    watchFiles: {
      paths: ['src/**/*.*'],
      options: {
        usePolling: true,
      },
    },
  },
};

最终的Webpack配置

const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = {
  mode: 'development',

  output: {
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },

  resolve: {
    alias: {
      '@scripts': path.join(__dirname, 'src/js'),
      '@styles': path.join(__dirname, 'src/scss'),
      '@images': path.join(__dirname, 'src/images'),
    },
  },

  plugins: [
    new HtmlBundlerPlugin({
      // path to templates
      entry: 'src/views/',
      js: {
        // output filename for JS
        filename: 'js/[name].[contenthash:8].js',
      },
      css: {
        // output filename for CSS
        filename: 'css/[name].[contenthash:8].css',
      },
    }),
  ],

  module: {
    rules: [
      {
        test: /\.(scss)$/,
        use: ['css-loader', 'sass-loader'],
      },
      {
        test: /\.(ico|png|jp?g|svg)/,
        type: 'asset',
        generator: {
          filename: 'img/[name].[hash:8][ext]', // save to file images >= 2 KB
        },
        parser: {
          dataUrlCondition: {
            maxSize: 2 * 1024, // inline images < 2 KB
          },
        },
      },
    ],
  },

  // enable HMR with live reload
  devServer: {
    static: path.resolve(__dirname, 'dist'),
    watchFiles: {
      paths: ['src/**/*.*'],
      options: {
        usePolling: true,
      },
    },
  },
};

结论

生成的HTML包含处理后的文件的输出文件名和与小图的内衬。所有处理过的文件均在dist/输出目录中。

<!doctype html>
<html lang="en">
<head>
  <title>Home</title>
  <link href="img/favicon.34fd67a8.ico" rel="shortcut icon">
  <link href="css/styles.f042b772.css" rel="stylesheet">
  <script src="js/main.b35246f4.js" defer="defer"></script>
</head>
<body>
  <h1>Homepage</h1>
  <img src="img/picture.7b396424.png" alt="picture">
  <!-- src contains "data:image/png;base64,iVBORwSU..." -->
  <img src="..." alt="icon">
</body>
</html>

仅使用一个html-bundler-webpack-plugin,您就可以很容易地设置WebPack来生成静态HTML文件。

View source code on GitHub

Try it out in browser