webpack 生产构建损坏服务端模板,关闭这类资产的 HTML 压缩
webpack 的内置 HTML 压缩会把尚未展开的服务端模板当成 HTML 处理。关闭对应压缩或为复制规则设置跳过标记,再用模板引擎验收完整结果。
把服务端模板交给 copy-webpack-plugin 原样复制,开发环境正常,升级 webpack 后生产模板却少了 %},应检查新增的 HTML 压缩步骤。复制插件保持输入不变,不代表后面的资产优化也保持它不变。
具体组合涉及 webpack 5.110.0 引入的内置 HTML 压缩;原报告在 5.110.0 至 5.110.3 核到相同损坏,使用 copy-webpack-plugin 14.0.0、webpack-cli 7.2.3 与 Node 24.19.0。输入类似:
<input type="text"{% if required %} required{% endif %}>
<div class="btn{% if active %} btn-active{% endif %}"></div>
这不是已经展开的标准 HTML。压缩器可能把模板中的 %} 当作属性名或 class token,并按 HTML 规则去重;模板结束符随之缺失。普通 HTML 中移除可省略的属性引号并非这里的问题,关键是模板语法被当成 HTML 数据处理。
最直接的做法是让这些模板避开内置 HTML 压缩。只希望停用这一类压缩时,可在相应版本支持的配置中使用:
module.exports = {
mode: 'production',
optimization: {
minimize: { html: false },
},
};
这个对象写法针对新增的按资产类型配置契约,仍保留 JavaScript 与 CSS 的压缩。已核 5.111.1 也支持对应的 minimizeOptions: { html: false } 选项。合并配置时选择一种清晰写法,不要用停止所有生产优化掩盖单类模板问题。
若普通页面仍需压缩,而只有一组复制的服务端模板要保持字节,可在复制规则中标记它们不再参与后续压缩:
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
new CopyPlugin({
patterns: [{
from: 'templates',
to: 'templates',
info: { minimized: true },
}],
}),
],
};
minimized: true 是给资产处理链的跳过标记,不表示模板已被某个工具正确压缩。这种精确规则适合同时保留普通 HTML 优化;应确认输出目录与原项目结构匹配,不要把所有资产都标记成已优化。
重新执行生产构建,逐字比较受影响模板输入与输出,尤其是条件属性、class 中的条件段以及空白位置不同的对照。然后通过真实服务端模板引擎编译并渲染条件为真、为假的两种结果。仅浏览器能打开某个静态 HTML,不能验收尚未经过服务端解释的语法。
内置压缩器后续修补过部分序列化行为,但不能由此保证 Django、Jinja、Twig 等任意模板语法都成为受支持 HTML。需要压缩最终页面时,更稳妥的处理顺序是先由模板引擎完成展开,再让适合该输出的工具处理合法 HTML。若模板仍被改写,查看是否另有 loader、插件或发布步骤参与,避免只关闭一个阶段便停止排查。


