创见博客
webpack 处理 HTML 资源:告别手动引入
七崽爱吃小饼干2026/09/15阅读 0专栏 webpack构建基础

前面几篇我们处理了 JS、CSS、Less、Sass、图片和字体,但别忘了还有一个关键角色——HTML。它既是页面入口,也是各种资源的引用入口。如果每次都手动建一个 index.html、再手动把打包出来的 bundle.js、CSS 文件一个个 script、link 进去,那每次产物改名(比如加了 [contenthash])都要跟着改,维护成本极高。

本文介绍如何让 webpack 自动生成并注入 HTML。

一、手动维护 HTML 的痛点

先看看「手动流」长什么样:

html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>demo</title>
    <link rel="stylesheet" href="../dist/css/main.a1b2c3d4.css" />
  </head>
  <body>
    <div id="app"></div>
    <script src="../dist/js/runtime.c9d0e1f2.js"></script>
    <script src="../dist/js/vendors.e5f6a7b8.js"></script>
    <script src="../dist/js/main.11223344.js"></script>
  </body>
</html>

问题一目了然:

  • 产物带 hash,每次构建文件名都变,HTML 必须同步修改。
  • 多入口 / 代码分割时,script 数量不固定,手工维护极易出错。
  • 依赖顺序(runtime、vendors、main)必须正确,否则运行时报错。
  • 开发环境和生产环境产物不同,得维护两份。

html-webpack-plugin 正是为解决这些问题而生:它以模板为基础,自动生成 HTML,并把当次构建产生的所有 JS、CSS 按正确顺序注入进去。

二、基本使用

1. 安装

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

2. 配置

js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/[name].[contenthash:8].js',
    clean: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html', // 以它为模板
    }),
  ],
};

模板里不需要再写任何 script / link:

html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>webpack demo</title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

执行 npm run build 后,dist/index.html 会自动带上正确路径的引用:

html
<script defer src="/js/main.11223344.js"></script>

注意默认注入的脚本带 defer,会等 HTML 解析完再执行,因此脚本放在 <head> 也没关系。

三、常用配置项

js
new HtmlWebpackPlugin({
  template: './public/index.html', // 模板路径
  filename: 'index.html',          // 输出文件名,可带目录
  title: '我的应用',                // 传给模板的标题
  inject: true,                    // 'head' | 'body' | false,默认 'head'
  favicon: './public/favicon.ico', // 自动注入 favicon
  minify: isProduction && {        // 生产环境压缩 HTML
    collapseWhitespace: true,
    removeComments: true,
  },
})

其中 title 等参数可以在模板中通过模板语法读取(默认支持 EJS 风格):

html
<title><%= htmlWebpackPlugin.options.title %></title>

四、多页面应用(MPA)

多入口时,为每个页面各 new 一个 HtmlWebpackPlugin,并用 chunks 指定该页面要引入哪些入口的产物:

js
module.exports = {
  entry: {
    app: './src/app.js',
    admin: './src/admin.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/[name].[contenthash:8].js',
    clean: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/app.html',
      filename: 'app.html',
      chunks: ['app'],
    }),
    new HtmlWebpackPlugin({
      template: './public/admin.html',
      filename: 'admin.html',
      chunks: ['admin'],
    }),
  ],
};

如果不写 chunks,每个 HTML 都会注入全部入口的脚本——这通常不是我们想要的,所以多页面场景务必显式指定。excludeChunks 则用于排除某些入口。

五、处理 HTML 模板内部的资源

默认情况下,模板里写的 <img src="./logo.png"> 不会被 webpack 处理,因为 HTML 是被当作纯文本读取的。要让 HTML 中的资源也走打包流程(压缩、hash 等),需要 html-loader:

bash
npm install html-loader --save-dev
js
module.exports = {
  module: {
    rules: [
      {
        test: /\.html$/i,
        use: 'html-loader',
      },
    ],
  },
};

这样模板里的 <img src="../assets/logo.png" /> 会被解析,交给对应的 asset module 处理,最终替换成正确的产物路径。

注意:html-loader 处理的是「模板内容」,html-webpack-plugin 处理的是「生成 HTML 页面」,两者可以配合使用,职责不同。

六、拷贝纯静态文件

有些文件不需要经过任何处理,只是原样拷贝到产物目录(如 robots.txt、CNAME、已压缩好的第三方库)。这类需求用 copy-webpack-plugin:

bash
npm install copy-webpack-plugin --save-dev
js
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        { from: 'public/robots.txt', to: 'robots.txt' },
        { from: 'public/static', to: 'static' },
      ],
    }),
  ],
};

区分原则很简单:需要参与构建(改路径、加 hash、压缩)的用 loader / plugin;原样拷贝的用 copy-webpack-plugin。

七、完整配置示例

js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const isProduction = process.env.NODE_ENV === 'production';

module.exports = {
  mode: isProduction ? 'production' : 'development',
  entry: {
    app: './src/app.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/[name].[contenthash:8].js',
    clean: true,
    publicPath: '/',
  },
  module: {
    rules: [
      { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
      { test: /\.html$/i, use: 'html-loader' },
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        type: 'asset',
        parser: { dataUrlCondition: { maxSize: 8 * 1024 } },
        generator: { filename: 'images/[hash][ext][query]' },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      title: 'webpack demo',
      favicon: './public/favicon.ico',
      inject: 'head',
      minify: isProduction && {
        collapseWhitespace: true,
        removeComments: true,
      },
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css',
    }),
  ],
};

小结

处理 HTML 资源的核心工具是 html-webpack-plugin:

需求方案
自动生成 HTML 并注入产物html-webpack-plugin
设置标题、favicon、压缩title / favicon / minify 选项
多页面分别注入不同入口多次 new + chunks
让模板里的图片走打包html-loader
原样拷贝静态文件copy-webpack-plugin

有了它,HTML 不再是需要手工同步的「易碎品」,而是构建流程中自动产出的一环。

评论
0/100