防止 HtmlWebpackPlugin 最小化生产中的换行符

Prevent HtmlWebpackPlugin from minimizing line breaks in production

这是我的 webpack 配置的相关部分:

plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      hash: false, // for testing purposes
      minify: false,
    }),    
  ]

尽管 minify: false,但如果 mode: 'production',HTML 变成一行。如果mode: 'development',则为多行。如果我更改 hash: true,那么它会变成一行带有用于缓存清除的查询字符串哈希值,这证明它读取了这个配置对象。

但是为什么它会截断换行符?我试过用specify an object设置collapseWhitespace等props代替minify: false,也没有效果,还是弄成一行。这是来自 package.json:

的版本列表
"devDependencies": {
    "@babel/cli": "^7.14.5",
    "@babel/core": "^7.14.6",
    "@babel/preset-env": "^7.14.5",
    "babel-loader": "^8.2.2",
    "babel-plugin-angularjs-annotate": "^0.10.0",
    "css-loader": "^5.2.6",
    "css-minimizer-webpack-plugin": "3.0.1",
    "html-loader": "^2.1.2",
    "html-webpack-plugin": "5.3.1",
    "mini-css-extract-plugin": "1.6.0",
    "postcss": "^8.3.4",
    "style-loader": "^2.0.0",
    "webpack": "5.39.0",
    "webpack-cli": "4.7.2",
    "webpack-dev-server": "3.11.2",
    "webpack-merge": "^5.8.0"
  }

编辑: 澄清一下,我指的是截断由 webpack 插入的动态包含之间的换行符,它们是对捆绑的 JS 和 CSS 文件的引用。所有这些文件都出现在一行中,无论是生产版本还是开发版本。好像没有办法逐行拆分,就好像是人工小心插入的一样。

为什么将每个动态包含在自己的行中很有用?例如,如果你想检查订单。目前唯一的选择是滚动 left/right 并尝试记住屏幕外显示的内容,这比从上到下阅读列表的认知负荷更大,因为所有内容都适合一个屏幕而无需滚动。

我看到您正在使用 html-loader。我的猜测是,自从您设置 minify: false 以来,在您的案例中进行缩小的不是 HtmlWebpackPlugin,而是 html-loader.

为了停止 html-loader 缩小,我们可以这样设置:

test: /\.html$/,
use: {
  loader: "html-loader",
  options: {
    minimize: false,
  },
}

但是,由于您还希望在生成的 [=58] 中包含 .js.css =].html文件分行我们运行成问题了。它们在同一行的原因是因为它们是由 HtmlWebpackPlugin 注入的,首先没有任何换行符。据我所知,没有我们可以设置的选项。

解决方案 1

步骤 1

我找到的最佳解决方案是使用 EJS 创建我们的模板文件(https://ejs.co/) instead of plain html. This allows us to write javascript directly into the template and gives us complete control over how the includes are injected. See this 答案。

首先将您的 html 模板文件重命名为 template.ejs。接下来在结束 </head> 标签之前添加:

for (const tag of htmlWebpackPlugin.tags.headTags) { %>
  <%= tag %><%
} %>

确保不要启用自动格式,因为我们依靠此处的换行符来生成正确的输出格式。根据您使用的 HtmlWebpackPlugin 版本,除了 headTags 之外,您还可以使用 bodyTags。在我看来,至少在最新版本中,他们选择将 script 包含在带有 defer 标志的头部以延迟执行,据我所知,这也是最有效的方法。

第 2 步

接下来我们需要在 HtmlWebpackPlugin 选项中更改模板的路径,并禁用包含的自动注入,如下所示:

new HtmlWebpackPlugin({
  template: "./src/template.ejs",
  inject: false
}),

步骤 3

因为 HtmlWebpackPlugin 有一个内置的回退 .ejs 解析器,我们不需要为此安装任何额外的加载器。唯一的问题是,由于您使用的是 html-loader 我假设您的模板文件中有资产,如图像等。如果您要创建一个新规则测试 .ejs 文件并使用 html-loader 来处理它们确实可以替换硬编码 src 包含动态的,这是加载程序的目的。但是,为 .ejs 文件指定一个加载器会阻止 HtmlWebpackPlugin 使用它的后备解析器,因此所有模板变量(那些在 <% %>) 未被解析。

然而我们实际上根本不需要html-loader。相反,您可以使用 EJSrequire 您在模板文件中的资产,如下所示:

<img src="<%= require('./assets/myimage.jpg') %>" />

解决方案 2

如果出于某种原因您需要/更喜欢使用 html-loader,还有另一种方法可以解决该问题。

因为 HtmlWebpackPluginhtml-loader 激活时不会使用它的后备 EJS 解析器,我们需要解析它使用另一个加载器,称为 ejs-loader。但是有一个小问题:当 html-loader 解析它生成的模板文件时 javascript。由于 ejs-loader 不希望收到 javascript 我们需要先将其转换回 EJS 格式。这可以通过另一个名为 extract-loader.

的加载器来完成

首先按照解决方案 1 中的步骤 1。接下来我们需要安装两个新的加载器:

yarn add -D ejs-loader extract-loader

之后我们将它们添加到 webpack config 中的 module rules。确保将 esModule 设置为 false 也用于 html-loader 因为 extract-加载程序无法处理:

module: {
rules: [
  {
    test: /\.ejs$/,
    use: [
    {
      loader: "ejs-loader",
      options: {
        esModule: false
      }
      },
      'extract-loader',
      {
      loader: "html-loader",
      options: {
        esModule: false
      },
    }]
  },

  ...

此设置以 html-loader 开始,它会自动解析任何 src 包含并将其转换为 require 语句。它生成 javascript 代码,extract-loader 将其转换回 EJS。最后 ejs-loader 解析 模板变量 ,在本例中是 js / [=58 的列表=]css包括。