直接用 html-eslint 不够用,因其默认仅校验纯 html5 语义,无法正确处理模板语法、自定义标签、非标准属性及内联脚本/样式,导致误报多或漏检真问题;需结合 eslint-plugin-html 纳入统一 eslint 流程,并通过自定义规则精准捕获 aria-* 拼写错误、缺失 role/tabindex、xss 风险等三类关键问题。

为什么直接用 html-eslint 不够用?
大型团队的 HTML 代码往往混杂着模板语法(如 Vue 的 v-if、React 的 JSX、后端模板引擎)、自定义组件标签、非标准属性(data-*、x-*)以及大量内联脚本/样式。官方 html-eslint 默认只校验纯 HTML5 语义,对这些扩展内容要么报错,要么完全忽略——结果就是:规则开得越严,误报越多;关得越松,真实问题越漏。
真正卡住团队的是三类问题:aria-* 属性拼写错误但未被识别、自定义标签缺失 role 或 tabindex、模板插值中意外引入 XSS 风险字符串(如未转义的 {{ rawHtml }})。这些必须靠自定义规则定位。
htmllint 插件怎么写才不踩坑?
别从零造轮子。优先基于 htmllint 的插件机制扩展,它比 html-eslint 更轻量、更易注入 AST 处理逻辑,且社区已有成熟插件生态(如 htmllint-plugin-vue)。
- 核心原则:只在
parse后 hook,不改原始 parser —— 否则和团队已有的 Prettier / ESLint 配置冲突 - 关键路径:监听
startTag和text事件,而非等完整 DOM 树生成 —— 大型 HTML 文件解析慢,流式处理才能保证 lint 速度 - 避坑点:
htmllint默认不解析 JS 内容,若需检查内联<script></script>中的危险操作(如innerHTML = ...),必须手动启用jsParser并传入@typescript-eslint/parser实例
如何让自定义规则和现有 ESLint 流程共存?
团队已有 eslint --ext .js,.jsx,.ts,.tsx 流程,HTML 检查不能另起一套命令或配置文件,否则 CI 会漏检、开发者会绕过。
推荐方案:用 eslint-plugin-html 将 HTML 文件纳入 ESLint 统一流程,再通过 processor 把 HTML 提取为 JS AST 交由自定义规则处理:
module.exports = {
processors: {
'.html': require('eslint-plugin-html').processors['.html'],
},
overrides: [{
files: ['**/*.html'],
processor: 'html/.html',
rules: {
'my-team/no-dangerous-innerhtml': 'error',
'my-team/required-aria-label': ['error', { 'ignoreTags': ['img', 'hr'] }],
},
}],
};
注意:eslint-plugin-html 默认把整个 HTML 当成字符串塞进 JS AST,所以你的自定义规则必须从 context.getSourceCode().text 中重新解析 HTML 片段,而不是依赖 ESLint 的 JS 节点遍历。
CI 中怎么避免 HTML Lint 成为瓶颈?
全量扫描 HTML 很慢,尤其当项目含上百个模板文件时。别用 npx eslint . --ext .html 这种全局扫描。
- 只检查变更文件:
lint-staged配合 Git diff,确保每次 PR 只跑修改过的 HTML - 跳过构建产物:
.eslintignore必须包含dist/、build/、node_modules/——htmllint对 minified HTML 的误报率极高 - 缓存解析结果:在自定义规则里加一层内存缓存(基于文件路径 + 文件 mtime),避免重复解析相同文件
最易被忽略的一点:HTML 规则的修复建议(fix function)必须是幂等的。比如自动补 aria-label,如果多次运行,不能反复叠加 aria-label="label" —— 否则 lint:fix 在编辑器保存时会无限循环。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











