首页
/ Svelte `{@html}` 标签完全指南:向组件注入原始 HTML 的原理、限制与安全实践

Svelte `{@html}` 标签完全指南:向组件注入原始 HTML 的原理、限制与安全实践

2026-09-04 20:12:45作者:卓艾滢Kingsley

在 Svelte 中,普通插值 {content} 会自动对字符串进行转义以保证安全,而当你需要渲染已经清洗过的富文本、Markdown 转换产物或第三方 HTML 片段时,就必须使用模板语法中的 {@html ...} 标签。本篇基于 Svelte 官方文档 08-@html.md,完整覆盖 {@html} 的用法、表达式约束、样式作用域规则,并结合 Svelte 编译器与运行时源码,讲清它在客户端渲染、服务端渲染和 hydration 三个场景下的实际行为,帮助你在安全地引入原始 HTML 的同时理解其底层实现。

基本用法:注入原始 HTML

要向组件中注入未经转义的原始 HTML,使用 {@html ...} 标签,把表达式写在标签内部:

<article>
	{@html content}
</article>

其中 content 是一个字符串表达式,其内容会被当作 HTML 直接解析并插入 DOM,而不是像 {content} 那样被转义为纯文本。

安全警告:官方文档明确要求——你必须对传入的字符串做转义/清洗处理,或者只填充自己可控的值,以防止 XSS 攻击。切勿渲染未经净化的内容(例如直接来自用户输入的 HTML)。

从 Svelte 编译器源码可以确认这一“零信任”设计:客户端实现 html.js 中明确注释了 @html 本质上就是 .innerHTML = ... 的等价物——由于安全考虑,innerHTML 赋值的 <script> 标签同样不会被执行,Svelte 运行时也没有做任何额外的脚本补执行逻辑。

表达式约束:必须是独立的合法 HTML

文档指出了两条硬性限制,这也是 {@html} 与普通标签插值最大的行为差异:

1. 内容必须是“可独立存在的合法 HTML”

下面这种写法是无效的,因为单独的 </div> 不是合法的 HTML 片段:

{@html '<div>'}content{@html '</div>'}

不能把一对开始/结束标签拆散到两个 {@html} 之间再“拼合”,每个 {@html} 表达式自身都必须能独立解析。仓库中的编译器错误测试 raw-mustaches-whitespace 同样验证了这类标签语法的严格性:写成 {@htmlfoo} 这种标签名后紧跟字符的形态会直接触发编译错误。

在解析阶段,{@html 是一个特殊的内建标签:解析器在 tag.js 中识别到 html 关键字后,会要求其后必须跟随空白、读取一个完整表达式、再闭合 },最终生成 AST 节点 HtmlTag

2. 不会编译其中的 Svelte 代码

{@html '<div onclick=...>'} 中的 onclick{#if} 等 Svelte 语法都不会被当作 Svelte 代码编译和绑定——它们只是普通文本,最终渲染出的 DOM 节点也不带 Svelte 的事件监听与更新逻辑。分析阶段的访问器 HtmlTag.js 中有一处关键处理:mark_subtree_dynamic(context.path) 会把包含 {@html} 的子树整体标记为动态子树,注释写明这是“为了修复非法 HTML”的必要操作——也就是说编译器不会尝试去“理解”注入的 HTML 结构,而是把控制权完全交给浏览器。

客户端渲染实现:模板元素、命名空间与 hydration

深入 客户端转换代码 与运行时 html() 函数,可以看清 {@html} 的三条核心执行路径:

1. 非受控路径(默认)——使用 <template> 包装解析。{@html} 独立出现在模板中时,编译器会先插入一个注释锚点(push_comment),运行时通过创建 <template> 元素并设置其 innerHTML 来解析 HTML,再把解析出的 DocumentFragment 移动到锚点前。这个设计的直接原因就是文档中“必须是独立合法 HTML”的约束:<template> 要求内容是完整片段。若 {@html} 位于 <svg><math> 上下文,运行时会自动改用带对应命名空间的 <svg> / <math> 包装元素,保证 SVG/MathML 内容不被降级成 HTML 命名空间的节点。

2. 受控路径(controlled)——直接操作父元素 innerHTML。{@html} 是某个父元素的唯一子内容时(例如 contenteditable 场景),运行时会直接 parent_node.innerHTML = value,绕开锚点注释。源码注释指出,这样也顺便解决了用户在 contenteditable 中手动删掉锚点注释导致更新失效的问题。仓库中的测试 html-tag-contenteditable 专门覆盖了这一场景。

3. 值未变化则跳过更新。 运行时用 if (value === (value = get_value() ?? '')) return 做快速去重:同一字符串值不会被重复写入 DOM,这是对频繁更新的富文本内容的一项重要性能保护。

hydration 时的行为: 在 hydration 阶段,运行时不会尝试修复服务端与客户端内容之间的差异——源码注释 明确写道“刻意不尝试修复不匹配,因为成本高且易错(而且内容不一致本身就是边缘情况)”。它只沿 DOM 寻找下一个锚点注释、把已有节点“认账”给 Svelte 管理;若找不到锚点则抛出 hydration mismatch 错误。相关测试可参考 html-tag-hydrationraw-repair

此外运行时还支持 TrustedHTML 对象:若传入的是 CSP 机制下的 TrustedHTML(如 policy.createHTML(...) 的返回值),会直接赋值给 innerHTML 而不做字符串强制转换,见 html.js

服务端渲染与异步表达式

在服务端(SSR)路径中,服务端 HtmlTag 转换 会生成 $.html(...) 调用,直接把(转义后的)字符串推入渲染输出流;若表达式内部包含 await(即标记为 async),则会包裹进一个异步子块并经由 $$renderer.push 输出——这对应 Svelte 的异步渲染能力。测试用例 async-html-tag(SSR)async-html-tag(客户端) 分别验证了两种端的异步 {@html} 行为。

样式:{@html} 内容对 Svelte 是“不可见”的

文档的 Styling 一节指出:以 {@html} 方式渲染的内容对 Svelte 而言是“不可见”的,因此不会获得 scoped styles(作用域样式)。下面的写法是无效的,aimg 的样式会被当作未使用的选择器处理(在产物中被移除):

<article>
	{@html content}
</article>

<style>
	article {
		a { color: hotpink }
		img { width: 100% }
	}
</style>

正确做法是使用 :global 修饰符来命中 <article> 内部的所有内容(对应 Svelte 文档中的 scoped styles 说明):

<style>
	article:global {
		a { color: hotpink }
		img { width: 100% }
	}
</style>

其根源在于:{@html} 的 HTML 是运行时才由浏览器解析生成的 DOM 节点,编译期的 CSS 分析器(如 css.js 所实现的剪枝逻辑)看不到这些节点,因此与它们相关的局部选择器会被判定为“未使用”。仓库中的 CSS 测试 global-blockglobal-local 正是验证 :global 与局部样式组合后的选择器输出。

实战建议小结

  • 只把已净化(如经 DOMPurify 类库清洗)或自己生成的 HTML 传入 {@html},用户原始输入必须先经过转义/白名单过滤;
  • 每个 {@html} 表达式必须能独立解析为合法 HTML,不能把标签拆到多个表达式中拼装;
  • 注入的内容不执行其中的 <script>,也不编译其中的 Svelte 语法,事件交互需通过事件委托等外部手段实现;
  • 依赖 CSS 时,用 :global 选择器命中注入内容,否则样式会被编译期剪枝丢弃;
  • 需要验证具体行为时,可参考 hydration 测试目录 中的 raw-*html-tag-* 系列用例,以及 runtime-runes 测试 中关于转义字符的处理示例。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384