htmlhint是专用于纯html静态分析的独立工具,基于自研解析器而非eslint生态,支持40+内置规则、三种配置方式(配置文件/命令行/内联注释)及自定义规则函数,可实现dom位置判断等精细控制,而eslint插件无法覆盖纯html结构问题。

htmllint 是目前唯一能支撑大型前端团队做精细 HTML 规则自研的成熟工具。它不依赖 ESLint 生态,而是基于独立解析器构建规则引擎,允许你逐条开关、参数化配置、甚至编写自定义规则函数——这是 html-eslint(基于 ESLint 的 HTML 插件)做不到的。
为什么 htmllint 能做精细规则控制,而 html-eslint 不行?
核心差异在规则粒度和执行模型:htmllint 的每条规则都是独立函数,接收 AST 节点和上下文,返回明确的错误对象;html-eslint 本质是把 HTML 当作字符串喂给 ESLint 解析器,再靠插件模拟语义,规则只能作用于标签名、属性名等粗粒度层级。
典型表现:
-
htmllint可以写规则:「禁止<img>标签在section内部使用,除非有loading="lazy"」 -
html-eslint最多做到:「禁止所有<img>缺少alt属性」——无法嵌套条件或 DOM 位置判断
htmllint 自定义规则的真实写法
在 .htmlhintrc 中启用自定义规则需两步:
- 在
rules中声明规则名和参数,例如:"my-custom-img-rule": true - 在项目中创建
lib/rules/my-custom-img-rule.js,导出函数:
module.exports = function (ruleConfig, ruleContext) {
return {
visitor: {
'tag:img'(node) {
const parent = node.parent;
if (parent && parent.tagName === 'section') {
const hasLazy = node.attrs.some(attr => attr.name === 'loading' && attr.value === 'lazy');
if (!hasLazy) {
ruleContext.report(node, 'img in section must have loading="lazy"');
}
}
}
}
};
};
注意:ruleContext.report() 的第三个参数可传 { severity: 'error' } 控制级别,node 包含完整位置信息,支持精准定位到行/列。
大型团队落地时最容易踩的三个坑
规则越细,越容易掉进执行陷阱:
-
htmllint默认不递归解析<template></template>或<svg></svg>子树,需要显式配置"attr-bans": ["style"]这类规则才生效于内嵌结构,否则规则失效但无报错提示 - 团队共用规则包时,
require()路径必须相对于.htmlhintrc所在目录,不是process.cwd()—— 很多人在 monorepo 中配错导致规则静默不加载 -
htmllint的--fix模式只支持极少数内置规则(如attr-lowercase),自定义规则一律不可修复,必须靠 CI 拦截 + 提示人工改,这点和 ESLint 完全不同
和 lint-staged 集成的关键细节
lint-staged 本身不关心规则内容,但它对 HTML 文件的处理链路必须严格对齐 htmllint 的能力边界:
- 不能写
"*.html": ["htmllint --fix", "prettier --write"]—— 因为--fix基本无效,反而掩盖真实错误 - 正确写法是分两层:先校验,再格式化,且校验失败必须阻断提交:
"*.html": ["htmllint", "prettier --write"] - 如果用了 Husky,确保
.husky/pre-commit中调用的是npx lint-staged而非直接npx htmllint,否则会绕过暂存区过滤逻辑
真正难的不是写规则,而是让规则在每个开发者本地、CI 环境、IDE 插件里行为完全一致——路径解析、AST 构建、错误上报这三环,任意一环错位都会导致“本地过 CI 不过”或“CI 过 IDE 报红”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











