直接用ast分析html代码质量需选用parse5或htmlparser2:parse5标准兼容、节点结构规范,适合语义检查;htmlparser2轻量事件驱动,适合流式处理但需手动组装ast。

直接用 AST 分析 HTML 代码质量,不是靠正则匹配标签或字符串查找,而是把 HTML 解析成结构化的树,再针对性检查节点关系、属性合法性、语义合规性。主流方案是用 parse5 或 htmlparser2 解析,再结合自定义遍历逻辑——而不是硬套 ESLint(它不原生支持 HTML)。
HTML AST 解析器选哪个:parse5 vs htmlparser2
parse5 更接近浏览器标准,生成的 AST 节点结构清晰、字段命名规范(如 tagName、attrs、children),适合做语义化检查;htmlparser2 更轻量、事件驱动,适合流式处理大文件,但 AST 需手动组装,节点字段名不统一(比如属性存于 attribs 而非 attrs)。
- 检查
<img>是否缺失alt属性?用parse5可直接访问node.attrs.find(a => a.name === 'alt') - 要检测嵌套层级过深的
<div>?<code>htmlparser2的onclosetag事件更易追踪深度,但需自己维护栈 - CI 环境中追求稳定性与可复现性,优先选
parse5;前端构建插件中需低内存占用,可考虑htmlparser2 -
<a></a>标签缺少href且无role="button"?遍历所有Element节点,过滤tagName === 'a',再检查attrs中是否存在合法href或显式role -
<h1></h1>到<h6></h6>是否跳级(如<h2></h2>后直接<h4></h4>)?需在遍历中记录上一个标题层级,比较当前parseInt(node.tagName[1]) - 内联样式(
style属性)是否含危险值(如expression(...))?解析node.attrs后对style值做子串扫描,而非整段匹配 - 该插件会把整个 HTML 当作字符串传给 ESLint,AST 是 JS 的,不是 HTML 的
- 若你只关心 script/style 标签里的 JS/CSS,它有用;但要做页面结构审计,必须换专用 HTML 解析器
- 部分团队误配
.eslintrc中的overrides,导致 HTML 文件被跳过解析,实际没生效 - 用
parse5.parseFragment()替代parse5.parse(),跳过文档类型、元信息等无关节点,提速约 30% - 避免在遍历中频繁调用
JSON.stringify(node)打印调试,这会触发全树序列化,极易 OOM - CI 流程里建议加超时限制:
timeout: 30s,防止某份异常大页卡死整个检查流水线
常见 HTML 质量问题怎么用 AST 检测
AST 的优势在于能跳过注释、忽略空白文本节点、准确识别嵌套关系,避免正则误判。比如检查表单控件是否都有关联 <label></label>,不能只看有没有 for 属性,还要确认对应 id 是否真实存在且唯一。
为什么不用 ESLint + eslint-plugin-html
eslint-plugin-html 实际是把 HTML 文件中嵌入的 JS 提取出来交给 ESLint 处理,它不分析 HTML 结构本身。比如它无法告诉你 <main></main> 缺少且页面有多个 <header></header>,也无法校验 ARIA 属性是否拼写正确(aria-labele 这种错漏)。
性能和工程落地要注意什么
单个 HTML 文件解析快,但批量扫描时,AST 构建和遍历开销明显。尤其当模板含大量重复结构(如表格行、列表项),节点数轻松破万,parse5 默认解析模式会吃掉大量内存。
真正难的不是写出第一个检查规则,而是让 AST 分析覆盖边界场景:服务端渲染的动态属性、框架指令(如 v-if、ngIf)、HTML 注释中的伪代码。这些都需要在解析阶段做预处理,否则 AST 根本不会包含它们——而这点,文档很少提。











