Svelte 组件还可以使用 `customElement: true` 编译器选项编译为自定义元素(即 Web 组件)。您应使用 ``[元素](svelte-options) 为组件指定一个标签名。 ```svelte

Hello {name}!

``` 您可以为不希望暴露的内部组件省略标签名,并像常规 Svelte 组件一样使用它们。组件的消费者如果需要,之后仍然可以为其命名,使用包含自定义元素构造函数的静态`element`属性,该属性在`customElement`编译选项为`true`时可用。 ```js // @noErrors import MyElement from './MyElement.svelte'; customElements.define('my-element', MyElement.element); ``` 一旦定义了自定义元素,就可以将其用作常规 DOM 元素: ```js document.body.innerHTML = `

This is some slotted content

`; ``` 任何[props](basic-markup#Component-props)都作为 DOM 元素的属性暴露(同时在可能的情况下,作为可读/可写的属性)。 ```js // @noErrors const el = document.querySelector('my-element'); // get the current value of the 'name' prop console.log(el.name); // set a new value, updating the shadow DOM el.name = 'everybody'; ``` 请注意,您需要明确列出所有属性,即在不声明 `props` 为 [组件选项](#Component-options) 的情况下,执行 `let props = $props()`,这意味着 Svelte 无法知道哪些属性应暴露为 DOM 元素的属性。 ## 组件生命周期 自定义元素通过包装方法从 Svelte 组件创建。这意味着内部 Svelte 组件不知道它是一个自定义元素。自定义元素包装器负责适当地处理其生命周期。 当创建自定义元素时,它包裹的 Svelte 组件不会立即创建。它只会在调用 `connectedCallback` 后的下一个 tick 中创建。在将自定义元素插入 DOM 之前分配给它的属性会被临时保存,然后在组件创建时设置,因此它们的值不会丢失。然而,对于在自定义元素上调用导出的函数,这种情况并不适用,它们只有在元素挂载后才能使用。如果您需要在组件创建之前调用函数,可以通过使用 [`extend` 选项](#Component-options) 来解决这个问题。 当使用 Svelte 编写的自定义元素被创建或更新时,shadow DOM 将在下一个 tick 中反映值,而不是立即。这样,更新可以批量处理,并且暂时(但同步地)从 DOM 中分离元素的 DOM 移动不会导致内部组件卸载。 内部 Svelte 组件在调用`disconnectedCallback`后在下个 tick 中被销毁。 ## 组件选项 当构建自定义元素时,您可以通过在``中将`customElement`定义为对象来调整几个方面,自 Svelte 4 以来。此对象可能包含以下属性: * `标签:字符串`:为自定义元素名称的可选 `标签` 属性。如果设置,则在导入此组件时,将使用文档的 `customElements` 注册表定义具有此标签名的自定义元素。 * `shadow`:一个可选属性,可以设置为`"none"`以跳过阴影根的创建。请注意,此时样式将不再封装,并且无法使用插槽 * `props`:一个可选属性,用于修改组件属性的某些细节和行为。它提供以下设置: * `属性:字符串`:要更新自定义元素的属性,你有两种选择:要么像上面所示,在自定义元素的引用上设置属性,或者使用 HTML 属性。对于后者,默认属性名是属性名的小写形式。通过将`属性:"< 期望的名称>"`赋值来修改它。 * `反射:布尔型`:默认情况下,更新的属性值不会反映回 DOM。要启用此行为,设置`反射:true`。 * `type: 'String' | 'Boolean' | 'Number' | 'Array' | 'Object'` :在将属性值转换为 prop 值并反射回时,默认假设 prop 值是一个`String`。这不一定总是准确的。例如,对于数字类型,使用`type: "Number"`来定义它。您不需要列出所有属性,未列出的属性将使用默认设置。 * `extend`:一个可选属性,期望其参数为一个函数。它将传递由 Svelte 生成的自定义元素类,并期望你返回一个自定义元素类。这在你有非常具体的需求,需要自定义元素的生命周期或想要增强类以使用[ElementInternals](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals#examples)以更好地集成 HTML 表单时非常有用。 ```svelte { // Extend the class so we can let it participate in HTML forms return class extends customElementConstructor { static formAssociated = true; constructor() { super(); this.attachedInternals = this.attachInternals(); } // Add the function here, not below in the component so that // it's always available, not just when the inner Svelte component // is mounted randomIndex() { this.elementIndex = Math.random(); } }; } }} /> ... ``` > \[!注意\] 虽然 TypeScript 在 `extend` 函数中受支持,但它受到限制:您需要在其中一个脚本上设置 `lang="ts"`,并且只能在其中使用 [可擦除语法](https://www.typescriptlang.org/tsconfig/#erasableSyntaxOnly)。它们不会被脚本预处理器处理。 ## 注意事项和限制 自定义元素可以是一种将组件打包以供非 Svelte 应用程序消费的有用方式,因为它们可以与纯 HTML 和 JavaScript 以及[大多数框架](https://custom-elements-everywhere.com/)一起工作。然而,有一些重要的差异需要注意: * 样式是*封装*的,而不是仅仅*作用域*的(除非您设置了`shadow: "none"`)。这意味着任何非组件样式(例如您可能在`global.css`文件中拥有的样式)都不会应用于自定义元素,包括带有`:global(...)`修饰符的样式。 * 而不是作为单独的.css 文件提取,样式以 JavaScript 字符串的形式内联到组件中 * 自定义元素通常不适用于服务器端渲染,因为阴影 DOM 在 JavaScript 加载之前是不可见的 * 在 Svelte 中,插槽内容会懒加载地渲染*。在 DOM 中,它会急切地渲染*。换句话说,即使组件的``元素位于一个`{#if ...}`块中,它也总会被创建。同样,在一个`{#each ...}`块中包含一个``也不会导致插槽内容被多次渲染。** * 过时的`let:`指令没有效果,因为自定义元素没有将数据传递给填充插槽的父组件的方法 * Polyfills 是支持旧浏览器的必需品 * 您可以在自定义元素中在常规 Svelte 组件之间使用 Svelte 的上下文功能,但不能在自定义元素之间使用。换句话说,您不能在父自定义元素上使用`setContext`,然后在子自定义元素中使用`getContext`来读取。 * 不要声明以`on`开头的属性或属性,因为它们的用法将被解释为事件监听器。换句话说,Svelte 将 `` 视为 `customElement.addEventListener('eworld', true)` (而不是`customElement.oneworld = true`)。