HTML质量校验必须嵌入Webpack构建输出阶段,而非pre-commit钩子,因html-webpack-plugin动态生成的HTML不在Git中;需用afterEmit钩子扫描dist目录产出文件,并注意CI环境配置、版本一致性及规则显式启用。

HTML 代码质量无法靠人工肉眼盯住,必须嵌入构建流程里自动跑。只要 html-webpack-plugin 在用,就该让 htmlhint 在每次构建时顺手扫一遍 —— 不是“要不要做”,而是“怎么让它不拖慢构建、不漏报问题”。
为什么不能只靠 pre-commit 钩子检查 HTML?
Git Hook 只能捕获你 git add 进暂存区的文件,但很多 HTML 是由 html-webpack-plugin 动态生成的(比如注入 hash 后缀的 script 标签、自动添加 manifest link),这些内容根本不在 Git 仓库里,pre-commit 完全看不到。
常见错误现象:本地 htmlhint 检查通过,CI 构建后扫描却报 tag-pair 错误或 attr-lowercase 失效 —— 实际是插件生成的 HTML 没被校验。
- 必须在校验环节接入 Webpack 构建输出阶段,而非源码提交阶段
- 若项目用了
html-webpack-plugin的template选项(如index.ejs),需确保htmlhint扫描的是最终产出的dist/index.html,而不是原始模板 -
htmlhint默认不支持 EJS/Handlebars 等模板语法,直接扫.ejs文件会大量误报,必须扫编译后的结果
如何在 webpack 构建后自动触发 htmlhint 扫描?
不能把 htmlhint 当作 Webpack 插件直接集成(它没有官方 loader 或 plugin),正确做法是用 webpack 的 afterEmit 生命周期钩子,在 dist 目录写完 HTML 后立刻执行校验。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 安装:
npm install --save-dev htmlhint - 在
webpack.config.js的plugins数组末尾加一段 hook:
plugins: [
new HtmlWebpackPlugin(),
{
apply: (compiler) => {
compiler.hooks.afterEmit.tap('HtmlHintPlugin', (compilation) => {
const { execSync } = require('child_process');
try {
execSync('npx htmlhint dist/*.html --format=unix', { stdio: 'inherit' });
} catch (e) {
// 不 throw,否则构建失败;让 CI 根据 exit code 判断是否阻断
}
});
}
}
]
注意:--format=unix 输出简洁格式,便于 CI 解析;stdio: 'inherit' 确保错误信息直接打到控制台,方便定位。
CI 中 htmlhint 报错但本地不报?检查这三处
典型现象:GitHub Actions 里 htmlhint 报 id-unique 错误,本地 npm run build 却安静通过。
-
htmlhint默认读取项目根目录下的.htmlhintrc,但 CI 环境可能没把该文件拉下来(尤其用了 sparse checkout 或 submodules)—— 检查 CI 日志里是否有Cannot find config file - CI 使用的 Node 版本可能比本地高,某些规则行为有差异(如
attr-no-duplication在 v1.10+ 对自定义属性更严格)—— 统一指定node-version: '20'并锁死htmlhint版本(如"htmlhint": "0.16.2") - 本地开发时
dist目录残留旧 HTML,而 CI 是 clean 构建 —— 错误实际来自老文件,新生成的 HTML 其实没问题;CI 必须加rm -rf dist && npm run build清理步骤
htmlhint 规则配置容易被忽略的细节
很多人复制粘贴网上示例配置,但没注意到几条关键规则默认是关闭的,而它们恰恰影响最大。
-
"attr-value-double-quotes": true—— 不开它,class='foo'就不会报错,但团队规范要求双引号,这条必须显式启用 -
"head-script-disabled": true—— 防止<script></script>写在里阻塞渲染,这个性能隐患常被忽视 -
"inline-script-disabled": true—— 禁止内联脚本,既是安全要求(CSP),也利于缓存分离,但默认不开启 - 若项目用
html-webpack-plugin注入 CDN 资源,需配合"attr-unsafe-chars": ["", "'", '"']防止模板注入,否则htmlWebpackPlugin.options.cdn传参可能绕过校验
最麻烦的不是规则本身,而是当多个 HTML 模板共用同一份 .htmlhintrc,但有的需要 doctype-html5,有的是邮件模板不用 DOCTYPE —— 这时得拆成多份配置,用 npx htmlhint --config .htmlhintrc-email src/email/*.html 分路径调用,别硬塞进一个文件里。



















