Svelte 組件也可以使用 customElement: true
編譯器選項編譯為自訂元素(又稱 Web 組件)。您應該使用 <svelte:options>
元素為組件指定標籤名稱。
<svelte:options customElement="my-element" />
<!-- in Svelte 3, do this instead:
<svelte:options tag="my-element" />
-->
<script>
export let name = 'world';
</script>
<h1>Hello {name}!</h1>
<slot />
您可以省略任何您不想要公開的內部組件的標籤名稱,並像一般的 Svelte 組件一樣使用它們。組件的使用者仍然可以在需要時使用包含自訂元素建構函式的靜態 element
屬性來命名它,而當 customElement
編譯器選項為 true
時,此屬性可用。
ts
importMyElement from './MyElement.svelte';customElements .define ('my-element',MyElement .element );// In Svelte 3, do this instead:// customElements.define('my-element', MyElement);
定義自訂元素後,它可以用作一般的 DOM 元素
ts
document .body .innerHTML = `<my-element><p>This is some slotted content</p></my-element>`;
預設情況下,自訂元素會使用 accessors: true
編譯,這表示任何 props 都會公開為 DOM 元素的屬性(以及在可能的情況下,可以作為屬性讀取/寫入)。
若要防止這種情況,請將 accessors={false}
新增至 <svelte:options>
。
ts
constel =document .querySelector ('my-element');// get the current value of the 'name' propconsole .log (el .name );// set a new value, updating the shadow DOMel .name = 'everybody';
組件生命週期永久連結
自訂元素是使用包裝器方法從 Svelte 組件建立的。這表示內部 Svelte 組件不知道它是一個自訂元素。自訂元素包裝器負責適當地處理其生命週期。
建立自訂元素時,它所包裝的 Svelte 組件並不會 立即建立。它只會在呼叫 connectedCallback
之後的下一個滴答中建立。指派給自訂元素(在它插入 DOM 之前)的屬性會暫時儲存,然後在建立組件時設定,因此它們的值不會遺失。不過,這不適用於呼叫自訂元素上的已匯出函式,它們只在元素掛載後才可用。如果您需要在建立組件之前呼叫函式,您可以使用 extend
選項 來解決此問題。
使用 Svelte 編寫的客製元素建立或更新時,影子 DOM 會在下次的 tick 反映值,而不是立即反映。這樣可以批次處理更新,而暫時(但同步)將元素從 DOM 分離的 DOM 移動不會導致內部元件卸載。
在呼叫 disconnectedCallback
後的下次 tick 中會銷毀內部 Svelte 元件。
元件選項永久連結
自 Svelte 4 起,在建立客製元素時,你可以透過在 <svelte:options>
中將 customElement
定義為物件來調整多個面向。此物件可能包含下列屬性
tag
:客製元素名稱的強制tag
屬性shadow
:可以設定為"none"
的選用屬性,以放棄建立影子根。請注意,樣式不再封裝,而且你無法使用插槽props
:修改元件屬性特定詳細資料和行為的選用屬性。它提供下列設定attribute: string
:若要更新客製元素的 prop,你有兩個選擇:設定客製元素參考上的屬性,如上所示,或使用 HTML 屬性。對於後者,預設屬性名稱是小寫的屬性名稱。透過指定attribute: "<desired name>"
來修改此設定。reflect: boolean
:預設情況下,更新的 prop 值不會反映回 DOM。若要啟用此行為,請設定reflect: true
。type: 'String' | 'Boolean' | 'Number' | 'Array' | 'Object'
:在將屬性值轉換為 prop 值並反映回 DOM 時,預設會假設 prop 值為String
。這可能並不總是正確的。例如,對於數字類型,請使用type: "Number"
定義它。你不需要列出所有屬性,未列出的屬性將使用預設設定。
extend
:一個可選屬性,其參數預期為函式。它會傳遞 Svelte 所產生的自訂元素類別,並預期您回傳一個自訂元素類別。如果您對自訂元素的生命週期有非常具體的要求,或是想要擴充類別以使用 ElementInternals 以獲得更好的 HTML 表單整合,這會非常實用。
<svelte:options
customElement={{
tag: 'custom-element',
shadow: 'none',
props: {
name: { reflect: true, type: 'Number', attribute: 'element-index' }
},
extend: (customElementConstructor) => {
// 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();
}
};
}
}}
/>
<script>
export let elementIndex;
export let attachedInternals;
// ...
function check() {
attachedInternals.checkValidity();
}
</script>
...
注意事項和限制永久連結
自訂元素可以是將元件封裝成非 Svelte 應用程式中可使用的元件的實用方式,因為它們會與原生 HTML 和 JavaScript 以及 大多數架構 搭配使用。不過,有一些重要的差異需要了解
- 樣式是封裝的,而不是僅範圍化(除非您設定
shadow: "none"
)。這表示任何非元件樣式(例如您可能在global.css
檔案中擁有的樣式)都不會套用至自訂元素,包含具有:global(...)
修飾詞的樣式 - 樣式並未作為一個獨立的 .css 檔案萃取出來,而是作為 JavaScript 字串內嵌到元件中
- 自訂元素通常不適合伺服器端渲染,因為在 JavaScript 載入之前,影子 DOM 是不可見的
- 在 Svelte 中,插槽內容會延遲渲染。在 DOM 中,它會立即渲染。換句話說,即使元件的
<slot>
元素位於{#if ...}
區塊內,它也會一直被建立。類似地,在{#each ...}
區塊中包含<slot>
也不會導致插槽內容被渲染多次 let:
指令沒有作用,因為自訂元素沒有辦法將資料傳遞給填滿插槽的父元件- 需要使用 Polyfill 來支援較舊的瀏覽器
- 你可以在自訂元素中的常規 Svelte 元件之間使用 Svelte 的 context 功能,但無法在自訂元素之間使用。換句話說,你無法在父自訂元素上使用
setContext
,然後在子自訂元素中使用getContext
讀取它。