校验必须在模块加载阶段同步执行,而非 fetch 回调或 mounted 钩子中——因 dom 已挂载,非法 html 会触发浏览器静默修复,导致 queryselector 失败、样式错乱、脚本不执行,且根因难追溯。

直接在模块加载阶段拦截非法 HTML 字符串,比在渲染时用 innerHTML 补救更可靠——因为后者已经破坏了 DOM 树结构,错误不可逆。
为什么不能等 fetch 回调里再校验 HTML?
模块加载完成、DOM 已挂载、事件监听器可能已注册,此时插入非法 HTML(如未闭合的 <div>、孤立的 <code><script></script>)会触发浏览器静默修复,导致 querySelector 失败、样式错乱、脚本不执行。排查时堆栈指向渲染层,但根因在上游模块的 HTML 生成逻辑。
- 校验必须发生在模块顶层
import后、任何 DOM 操作前 - 禁止把校验逻辑写在
fetch().then()或组件mounted钩子中 - 若服务端返回的是片段(如
<p>Hi</p> <div class="meta"></div>),必须先用<div>${htmlStr}</div>包裹,否则DOMParser会丢弃并列节点
用 DOMParser 而不是正则或字符串判断
DOMParser().parseFromString(htmlStr, 'text/html') 是唯一能真实模拟浏览器解析行为的方式。正则无法处理嵌套、命名空间、自定义元素或条件注释;字符串 includes('<script>')</script> 会误报 CDATA 内容或注释里的关键词。
- 检查是否解析成功:
doc.body.children.length === 0 && htmlStr.trim() !== ''→ 应throw new Error('Invalid HTML structure') - 不要 fallback 到空内容或
console.warn,阻断执行才能暴露问题 - 导出纯函数如
safeHtmlParse,只接收字符串、返回DocumentFragment或抛错,不操作 DOM
ES 模块内封装 + 显式渲染契约
把校验逻辑放在全局或内联脚本里,等于放弃模块隔离优势。ES 模块作用域是防止规则被覆盖或绕过的天然防线。
- 模块 A 不该导出
generateCardHtml(user)这类纯字符串函数 - 应导出
renderCard(el, user),内部完成校验 +el.appendChild(fragment) - 若需复用 HTML 字符串(如 SSR),模块 A 必须同时导出
isSafeHtml类型断言函数,供 TS 编译期辅助 - 禁止跨模块隐式传递 HTML 字符串——这会让错误堆栈失真、责任边界模糊
最易被忽略的一点:校验函数必须在模块顶层同步执行,不能包裹在异步回调、setTimeout 或事件监听器里。否则模块加载链路已完成,校验就失去意义。











