不能用正则批量替换或,因为正则无法区分嵌套层级和属性上下文,会误匹配注释内标签或被内部标签打断,而ast能准确识别节点类型、父子关系及是否在注释中。

直接用 AST 解析器做替换,比正则安全得多;但 parse5 和 htmlparser2 的节点结构不一致,attrs 字段类型、isSelfClosing 判断逻辑、文本节点过滤方式稍有不同,选错就容易生成非法 HTML。
为什么不能用正则批量替换 <font></font> 或 <center></center>
正则无法区分嵌套层级和属性上下文。比如 <font color="red"><b>hello</b></font> 里, 前的 <b></b> 会打断贪婪匹配,导致截断或漏改;更糟的是,<!-- <font> --> 这类注释里的标签也会被误匹配。而 AST 能准确识别每个节点类型、父子关系和是否在注释中。
parse5 中安全替换废弃标签的实操要点
用 parse5 替换时,必须手动遍历所有 Element 节点,并检查 node.tagName 是否在废弃列表里(如 "font"、"center"、"marquee"):
- 废弃标签不等于要删掉——
<font color="red">text</font>应转成<span style="color:red">text</span>,不是简单删标签 -
node.attrs是数组,不是对象;提取color属性得遍历node.attrs.find(a => a.key === "color")?.value - 替换后的新节点必须显式设置
isSelfClosing: false,否则parse5.serialize()可能输出<span></span>这种非法写法 - 文本节点(
node.nodeType === 3)不能调用getAttribute,遍历时需先判断node.nodeName === "#text"
如何保留原始缩进与注释不被破坏
parse5 默认解析会丢弃注释和空白文本节点。若需保留,必须传入 { treeAdapter: parse5.DefaultTreeAdapter } 并启用注释支持:
const document = parse5.parse(html, {
treeAdapter: {
...parse5.DefaultTreeAdapter,
createCommentNode: (data) => ({ nodeName: '#comment', data, children: [] })
}
});
但注意:这样构造的 AST 不再兼容 parse5.serialize() 直接输出,需自己实现序列化逻辑,或改用 htmlparser2 + dom-serializer 组合,后者对注释和空白更友好。
真正难的不是“怎么换”,而是“换完是否还符合语义”——比如把 <center></center> 换成 <div style="text-align:center"> 后,如果它包着一个 <code><table>,那响应式布局可能就崩了;这时候得结合 CSS 类名或媒体查询一起改,AST 只负责结构,不负责样式适配。</table>











