<main>必须唯一且为<body>直接子元素,禁止嵌套于<section><header><nav>等分区根元素内,否则屏幕阅读器跳过主内容、SEO降权、Lighthouse报duplicate-main错误。

发布前夕 HTML 崩溃,90% 不是浏览器问题,而是校验缺失导致的隐性结构错误——比如 <main> 嵌套在 <section> 里直接失效、<meta charset> 被 CSS <link> 挡在后面导致 Safari 解码乱码、lang 值写成 "zh" 而非 "zh-CN" 让屏幕阅读器跳过整页。这些错误在本地开发时几乎不报错,但一进 CI 或真机环境就触发渲染异常或无障碍中断。
HTMLHint 配置必须覆盖语义结构规则
默认配置只查基础语法,对 <main>、<header>、<nav> 的嵌套合法性完全不敏感。必须手动启用语义化校验规则:
- 开启
html-req-lang:强制<html lang="...">存在且非空 - 启用
html-req-lang-attr:防止lang="zh"这类宽泛值(需匹配zh-CN、en-US等 IETF 标准) - 添加
heading-levels:检查<h1>到<h6>是否跳跃或缺失主标题 - 启用
id-unique+attr-no-duplication:避免重复id导致 JSdocument.getElementById()返回错误节点
配置片段示例:
{
"html-req-lang": true,
"html-req-lang-attr": true,
"heading-levels": true,
"id-unique": true,
"attr-no-duplication": true
}
字符编码与资源加载顺序必须被静态捕获
<meta charset="UTF-8"> 必须出现在 <head> 前 1024 字节内,否则 Safari 和部分 WebView 会按 ISO-8859-1 解析后续中文,导致乱码崩溃。HTMLHint 默认不检查位置,需配合自定义规则或使用 htmlhint --rule "meta-charset-require:true" 强制触发。
立即学习“前端免费学习笔记(深入)”;
-
<meta charset>必须在所有<link rel="stylesheet">和<script>之前 - 禁止在
<meta charset>前插入任何非空白字符(包括注释、BOM、空行) - 若用构建工具注入 CSS/JS,确保其插件不破坏
<meta>位置(如 Webpack HtmlWebpackPlugin 的inject: 'head'可能插错位置)
CI 流程中 HTML 校验必须阻断而非警告
把 HTMLHint 当作 ESLint 一样设为 CI 失败条件,而不是生成报告后人工翻查。否则发布前最后一刻才发现 <main> 失效,修复成本远高于提前拦截。
- 在
package.json中定义脚本:"lint:html": "htmlhint \"src/**/*.html\" --config .htmlhintrc" - CI 配置中加入:
npm run lint:html || exit 1(不能只写npm run lint:html) - 禁用
--quiet或--format=unix等弱化输出的参数,确保每条错误都可定位到行号 - 对动态模板(如 EJS、Handlebars),需先编译再校验,否则
<% if (x) { %><div><% } %>类结构无法被静态分析识别
最易被忽略的是:HTML 结构错误在现代浏览器中往往“看起来正常”,但会在 iOS VoiceOver、NVDA 或低版本 Android WebView 中直接跳过关键区域——这种崩溃不会抛 JS 错误,也不会触发 Sentry,只能靠严格校验卡在发布前。



















