直接用正则校验HTML结构会失效,因其无法处理嵌套、自闭合标签、注释、CDATA及属性换行等复杂语法,且不能建模节点层级与作用域;必须基于标准解析器(如parse5)生成AST后遍历校验。

为什么直接用正则校验 HTML 结构会失效
正则匹配 <div> 和 </div> 看似简单,但嵌套、自闭合标签、注释、CDATA、属性换行都会让模式崩坏。浏览器解析 HTML 时依赖完整的词法 + 语法分析,而正则根本无法建模节点层级与作用域。硬拦截必须基于真实解析结果,否则漏判或误杀都不可接受。
真正可靠的起点是把 HTML 字符串喂给标准解析器,生成 AST(如 parse5 或 htmlparser2 的树结构),再遍历校验。不是“模拟解析”,而是复用浏览器同源逻辑。
用 parse5 构建可拦截的 AST 校验流程
parse5 是目前最贴近 HTML 标准的解析器,输出符合 DOM 规范的节点树,且支持错误恢复(即即使 HTML 有错也能产出可用 AST),这对拦截前的容错判断很关键。
实操建议:
- 用
parse5.parseFragment()而非parse5.parse(),避免因缺失<html>或<body>头部导致解析失败——多数用户提交的是片段 HTML - 校验前先检查
document.errors.length > 0,若存在严重解析错误(如未闭合的<script>嵌套在属性值里),直接拒绝,不进后续规则 - 遍历时优先用
treeAdapter提供的getChildren()和getNodeName(),别手动读nodeName字段——不同节点类型(Element/Text/Comment)字段名不一致
硬拦截的关键校验点:哪些结构必须拒绝 硬拦截不是美化或警告,是“不通过就截断”。以下几类问题一旦命中,应立即终止渲染并返回错误:
常见错误现象:
-
<input>出现在<head>里——实际会被提升到<body>,但业务要求结构语义严格,不允许 - 多个
<body>或<html>标签——说明文档碎片拼接混乱,可能引发脚本执行错位 -
<script>标签内含未转义的</script>字符串——AST 中会提前截断节点,导致后续内容丢失 - 使用非标准自闭合标签如
<my-component/>且未声明命名空间——parse5默认按 HTML 模式解析,这类标签会被当作普通开标签,缺少闭合即报错
性能与兼容性取舍:校验时机和范围 全量 AST 遍历在 Node.js 服务端做没问题,但在前端做硬拦截(如富文本提交前)就得控制深度。不要递归所有子孙节点——90% 的违规集中在顶层容器和首层子元素。
参数差异与影响:
- 用
parse5.TreeAdapters.default即可,无需定制 adapter;但若需兼容 IE 的旧 HTML 解析行为,得切到parse5.TreeAdapters.htmlparser2,不过它不保证错误恢复能力 - 校验函数里避免调用
textContent或innerHTML——这些是序列化操作,会触发重建字符串,比直接读childNodes慢 3–5 倍 - 对超长 HTML(>1MB),先用
parse5.parseFragment(html, { treeAdapter: ... })的 streaming mode 启动,配合maxBufferSize限流,防 OOM
<slot> 是否允许出现在 <template> 外,取决于框架版本和编译阶段。AST 只提供事实,裁决权必须下沉到业务 Schema 层。



















