首页 / Svelte 5 入门教程 / 自定义元素(Web Components)

Svelte 5 入门教程

自定义元素(Web Components)

本教程共 50 篇 · 第 40 篇 · 更新于 2026-08-05 · 约 5 分钟阅读

SvelteSvelte 5Web Components自定义元素Custom Elements

本节目标:学会把 Svelte 组件编译成 Web Components(自定义元素),掌握 Shadow DOM、属性映射、$host rune 和组件选项配置。

什么是自定义元素

Web Components 是浏览器的原生标准,让你创建可复用的自定义 HTML 标签。Svelte 组件可以编译成自定义元素,这样即使不用 Svelte 的项目也能用你的组件。

比如你写了一个 <my-button> 组件,别人在纯 HTML 页面里写 <my-button>点击</my-button> 就能用。

基本用法

在组件里用 <svelte:options> 声明它是一个自定义元素,并指定标签名:

<svelte:options customElement="my-button" />

<script>
	let { label = '按钮' } = $props();
</script>

<button>{label}</button>

导入这个组件后,它会自动注册为自定义元素,你可以在任何地方使用:

import './MyButton.svelte';

document.body.innerHTML = '<my-button label="提交"></my-button>';

也可以在 Svelte 模板中当普通标签用:

<my-button label="提交" />

Props 和属性映射

自定义元素的 props 会同时暴露为 JavaScript 属性和 HTML 属性:

const el = document.querySelector('my-button');

// 通过属性读取和设置
console.log(el.label);    // '提交'
el.label = '取消';        // 更新组件

// 通过 HTML 属性设置
el.setAttribute('label', '保存');
Note

必须显式声明所有 props(如 let { name } = $props()),不能只写 let props = $props()。Svelte 需要知道每个 prop 的名字才能把它们暴露到 DOM 元素上。

Shadow DOM

自定义元素默认使用 Shadow DOM 来隔离样式。这意味着组件的 <style> 不会泄漏到外部,外部样式也不会影响组件内部。

可以配置 Shadow DOM 的行为:

<svelte:options
	customElement={{
		tag: 'my-card',
		shadow: 'open'   // open / closed / none
	}}
/>
说明
'open'Shadow DOM 开放模式,外部 JS 可通过 element.shadowRoot 访问
'closed'Shadow DOM 封闭模式,外部无法访问内部 DOM
'none'不创建 Shadow DOM,样式不隔离,不能用 slot
Tip

开发时用 'open' 方便调试,生产环境可用 'closed' 保护内部结构。也可以根据环境变量动态选择:shadow: import.meta.env.DEV ? 'open' : 'closed'

Props 高级配置

通过 props 选项可以控制每个 prop 的行为:

<svelte:options
	customElement={{
		tag: 'my-input',
		props: {
			value: {
				attribute: 'data-value',  // 自定义 HTML 属性名
				reflect: true,             // 值变化时同步回 HTML 属性
				type: 'String'             // 类型转换
			},
			count: {
				type: 'Number'             // 属性值转成数字
			}
		}
	}}
/>

<script>
	let { value = '', count = 0 } = $props();
</script>

<input bind:value />
<p>计数:{count}</p>

type 支持的值:'String''Boolean''Number''Array''Object'。HTML 属性是字符串,type 决定了属性值和 prop 值之间的转换方式。

$host rune

$host() 返回当前自定义元素的 DOM 节点。当你需要在组件内部访问宿主元素时用它:

<svelte:options customElement="my-tooltip" />

<script>
	let { position = 'top' } = $props();

	function handleClick() {
		// $host() 返回 <my-tooltip> 元素
		const el = $host();
		el.dispatchEvent(new CustomEvent('activated', {
			detail: { position }
		}));
	}
</script>

<button onclick={handleClick}>激活</button>
Note

$host() 只能在编译为自定义元素的组件中使用。在普通组件中调用 $host() 会导致编译错误。

事件分发

自定义元素通过 CustomEventdispatchEvent 来发送事件。外部用 addEventListener 监听:

const el = document.querySelector('my-tooltip');
el.addEventListener('activated', (e) => {
	console.log(e.detail.position); // 'top'
});
Warning

不要用 on 开头的 prop 名(如 onclickonselect)。浏览器会把 on 开头的属性当作事件监听器。比如 oneworld 会被解析成监听 eworld 事件。

extend 选项

extend 让你自定义元素类,实现高级功能(如表单关联):

<svelte:options
	customElement={{
		tag: 'my-form-input',
		extend: (BaseClass) => {
			return class extends BaseClass {
				static formAssociated = true;

				constructor() {
					super();
					this.internals = this.attachInternals();
				}

				// 在组件挂载前就可调用的方法
				setValue(value) {
					this.internals.setFormValue(value);
				}
			};
		}
	}}
/>

<script>
	let { attachedInternals } = $props();

	function check() {
		attachedInternals.checkValidity();
	}
</script>

<input type="text" />

生命周期

自定义元素的生命周期和 Svelte 组件略有不同:

  • 创建:元素被 document.createElement 或 HTML 解析创建时,内部 Svelte 组件不会立即创建
  • 挂载:元素插入 DOM 后的下一个 tick,内部组件才创建
  • 卸载:元素从 DOM 移除后的下一个 tick,内部组件才销毁

这意味着在元素插入 DOM 前设置的 props 会被暂存,等组件创建后再应用。

注意事项

用 Svelte 写自定义元素时要注意:

  • 样式是封装的(Shadow DOM),全局 CSS 选择器无法直接影响组件内部,但 CSS 自定义属性(CSS variables)可以穿透 Shadow DOM 边界
  • 样式以 JS 字符串内联到组件中,而不是提取成单独 CSS 文件
  • 自定义元素不适合 SSR,因为 Shadow DOM 在 JS 加载前不可见
  • slot 内容是即时渲染的,不受组件内 {#if} 条件控制
  • Context 不能跨自定义元素传递,只能在同一个自定义元素内部的 Svelte 组件间使用

本节回顾

  • <svelte:options customElement="tag-name" /> 把组件编译为 Web Component
  • Props 自动暴露为 DOM 属性和 HTML 属性,必须显式声明
  • props 选项控制属性名映射、值反射和类型转换
  • Shadow DOM 默认开启,配置 'open'/'closed'/'none'
  • $host() 访问宿主元素,用于分发自定义事件
  • extend 选项可扩展元素类,实现表单关联等高级功能
  • 注意样式封装、slot 行为和 SSR 限制等差异