v-html是Vue中唯一能直接渲染HTML字符串的指令,但存在XSS风险;必须配合DOMPurify等工具净化不可信内容,且不可用于slot标签内,应在外层元素使用。

Vue 2 中 v-html 是唯一安全的解法
Slot 内容默认被当作纯文本渲染,所有 HTML 标签都会被转义(比如 <strong>hello</strong> 显示为字面量),这不是 bug,是 Vue 的默认防护机制。想让 slot 内容作为 HTML 解析,必须显式声明——v-html 是唯一推荐方式,且只能用在普通元素上,不能直接写在 <slot> 标签里。
常见错误是试图这样写:
<slot v-html="content"></slot>这无效,
<slot> 是编译时占位符,不支持指令绑定。
- 正确做法:在外层包裹一个
<div>或<span>,再用v-html绑定 slot 插入的内容 - 如果使用具名 slot,需确保父组件传入的是字符串(不是 VNode 或对象)
-
v-html会完全替换元素子节点,原有 slot 默认内容会被清空 - 务必确认内容来源可信,否则有 XSS 风险——Vue 不做二次过滤
Vue 3 Composition API 下仍用 v-html,但要注意作用域
Vue 3 中 slot 本质仍是函数返回 VNode,所以「slot 内容被转义」的问题根源没变。如果你在 setup 里通过 slots.default() 拿到的是 VNode 数组,那它本来就不会被转义;但如果你传的是字符串(比如 <MyComp><div><b>test</b></div></MyComp>),模板编译后仍会转义为文本。
- 解决方式不变:用
<div v-html="slots.default?.()[0]?.children || ''"></div>——但注意,这仅适用于插槽内容是单一字符串节点,复杂结构会丢失响应性 - 更稳妥的做法是让父组件直接传 HTML 字符串,子组件用
v-html渲染,而非依赖 slot 编译结果 - 不要用
render函数手动h('div', { innerHTML: str }),Vue 3 不支持这种写法,会报错
为什么不用 innerHTML 直接操作 DOM?
有人试过在 mounted 里用原生 innerHTML 覆盖 slot 容器内容,看似能绕过转义,但问题很多:
- 破坏 Vue 的响应式更新机制,后续数据变化不会同步到该区域
- 如果 slot 内含子组件或指令(如
v-if),它们根本不会被初始化 - SSR 场景下服务端渲染和客户端 hydrate 会不一致,触发 warning 甚至崩溃
- Vue Devtools 无法追踪这部分 DOM,调试困难
第三方库如 marked 渲染 Markdown 时怎么避免双重转义?
典型场景:用 marked 把 Markdown 转成 HTML 字符串,再传给 slot——结果发现 <p><em>xxx</em></p> 被当作文本显示。这是因为 slot 接收后又被 Vue 转义了一次。
- 关键点:不要把 Markdown 字符串塞进 slot,而是在父组件里先用
marked转成 HTML,再用v-html渲染 - 如果必须通过 slot 传递,子组件要主动提取字符串并用
v-html输出,不能直接<slot></slot> - 注意
marked默认开启 sanitize,若关闭了,务必自行过滤危险标签(如<script>),否则v-html会放大风险
真正麻烦的不是怎么取消转义,而是得时刻分清:内容在哪一层生成、在哪一层消费、是否经过 Vue 编译管道——漏掉任意一环,就容易白忙活。

















