HTMLHint无法实现AST级嵌套语义检查,因其规则仅基于token流或单行文本,不提供parentNode、children等AST节点属性;真需校验如“img是否在figure内”等语义,必须改用parse5构建真实AST并手动遍历。

HTMLHint 不能做 AST 级规则,别白费劲写“嵌套语义”检查
HTMLHint 的规则函数接收的是 token 流或单行文本,不是 AST 节点对象。你没法调用 node.parentNode、node.children 或判断 node.isSelfClosing —— 这些属性根本不存在。所有所谓“组件内 img 必须带 alt”“template 下的 button 要有 aria-label”的需求,在 HTMLHint 里注定失败。
常见错误现象:
- 写了遍历逻辑却收不到子节点信息,
node.children是undefined - 试图用
node.tagName === 'FIGURE'判断父级,结果node根本没tagName字段 - 文档里看到“支持自定义规则”,误以为能复刻 ESLint 风格的上下文感知
它真正能干的只有两类事:
- 正则匹配局部模式:比如
alt-require查<img>是否含alt属性(不关心它在不在<figure>里) - 状态机驱动的配对检查:比如
tag-pair确保<div>和</div>出现顺序合法(不校验是否被<p>包裹)
真要查嵌套语义?得换 parse5 + 手动遍历
想检查 “所有 <img> 是否在 <figure> 内且紧邻 <figcaption>”,就得放弃 HTMLHint,用 parse5 构建真实 AST,再写 DFS 遍历逻辑。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 安装:
npm install parse5 - 解析后拿到的是标准 DOM-like 树结构,
node.childNodes、node.parentNode全都有 - 每条规则都要自己写完整遍历 + 条件判断,没有现成 rule runner
- 性能要注意:大文件(>1MB)下
parse5.parse()可能卡顿,建议加try/catch并设超时
示例片段(只查 <img> 是否在 <figure> 下):
const parse5 = require('parse5');
const document = parse5.parse(html);
function findImgInFigure(node) {
if (node.nodeName === 'IMG' && node.parentNode?.nodeName === 'FIGURE') {
return true;
}
return node.childNodes?.some(child => findImgInFigure(child)) || false;
}
Vue/React 项目更省事:转成 JSX/SFC 再交给 ESLint
如果你的 HTML 实际是 Vue 单文件组件(.vue)或 React JSX(.tsx),别硬啃原生 HTML 解析 —— 把模板交给对应生态的 lint 工具更可靠。
适用场景:
-
eslint-plugin-vue能识别<MyButton>组件内是否漏了role -
eslint-plugin-react可检查<img>在<article>下是否缺失alt(依赖 JSX 层语义) - 前提是你已配置好
@vue/compiler-sfc或@babel/preset-react,能把模板编译成 AST
注意陷阱:
- 纯 HTML 文件(
.html)无法直接喂给 ESLint,必须先转格式或走构建流程 - 第三方组件库的自定义标签(如
<ant-design-button>)需额外配置settings.react.extendedAPIs才能识别
运行时 DOM 对比才是暴露真实问题的关键
静态分析永远看不到 JS 动态拼接的 HTML 错误。比如 CMS 后台保存后,前端用 innerHTML += '<div><p>text</div>',W3C 验证和 HTMLHint 全部通过,但浏览器实际渲染出错嵌套。
正确做法是在 DOMContentLoaded 触发瞬间,用 fetch(location.href, { cache: 'no-store' }) 拉原始 HTML,再用 new DOMParser().parseFromString() 解析对比 document.documentElement.outerHTML —— 这才能发现服务端漏渲染、CDN 缓存脏数据等问题。
容易被忽略的细节:
-
document.documentElement.outerHTML返回的是浏览器“修复后”的 DOM,自动补闭合、拆非法嵌套,完全掩盖原始缺陷 - 若页面启用了 CSP
connect-src,fetch可能被拦截,得 fallback 到从<script type="application/json">提取可信片段作锚点 - 第三方 SDK 注入的
<div class="ad-banner">若没闭合,仅靠静态扫描永远扫不出来



















