這是〈Svelte 從小白到入門〉系列的第十二集,各集連結如下:


Slot

在 HTML 我們常會用 <div> 做為容器,內部放入其他的元素,而在 Svelte 元件,也有類似的用法,稱為 slot,顧名思義,一個 Svelte 元件內有了 slot,當此元件被使用時,可以被置入其他的元素。

Slot 基礎

一個最簡單的內含 slot 的 Svelte 元件:

在 Box 元件內,先預放 <slot></slot>,父元件調用 Box 時,就可以在 <Box></Box> 中間置入其他元素。

在 App 中的第一個 Box,我們置入了 slot 的內容,所以 Box 內預留的「no content was provided」就會被取代掉。

另外注意到上例中 <p> 的樣式是在 App 定義的,而不是在 Box 內定義的。

在 App 的第二個 Box 並位置入 slot 內容,所以 Box 內預留的「no content was provided」就會顯示出來。

在 App 的第三個 Box 是巢狀置入,slot 不僅可置入一般元素,也可以置入 Svelte 元件。那能不能惡搞做出無限迴圈呢?答案是不行的,殘念です…,Svelte 在建置期間會警示並阻擋無限迴圈的元件調用。

所以要怎麼做出下面的 Woo~! That’s funky~~~ 效果呢?嗯…?

具名的 Slot

當一個 Svelte 元件有超過一個 slot 時,那就要為 slot 命名,以便調用時正確置入內容。

只要為 slot 加上 name 屬性就是個具名的 slot:

<slot name="address"></slot>

完整的範例如下:

在 Box 裡面有三個 slot:

  • <slot name="name">
  • <slot name="address">
  • <slot name="email">

而在 App 這邊我們只填入了姓名與地址的 slot,剩下的 email 未填,以預留的「Unknown email」顯示。

一個典型的具名的 slot 用法是將名稱做為屬性填入,以上面的例子來說是這樣:

<span slot="address">43 Wallaby Way</span>

$$slots

在此之前,我們曾經在第二集介紹 props 時見過 $$props$$props 是存放所有傳進元件內的 props 的變數,而 $$slots 則是所有傳進元件內的 slot 內容的變數,兩者的命名模式與語意是類似的。

$$slots 它裡面存放了所有被傳進來的 slot 內容,當我們想要在元件的任意位置調用某個 slot 內容可以用它,或者是我們也可以在元件內根據 $$slots 的值來調整該元件要展現的內容或樣式。

下面的例子裡,「Add Typescript support」有留言,因此卡片右上角有小圓點,並且卡片下方有顯示留言,而「Update document」沒有留言,因為那個 team 如同我們一樣,嘴巴上都講文件很重要,但其實沒人真正在乎,所以沒有小圓點,也沒有顯示留言區塊:

在 Project 元件中,注意到這一區塊:

<article class:has-discussion={$$slots.comments}>
	<div>
		<h2>{title}</h2>
	</div>
	{#if $$slots.comments}
		<div class="discussion">
			<h3>Comments</h3>
			<slot name="comments"></slot>
		</div>
	{/if}
</article>

在 CSS class 方面,我們用 {$$slots.comments} 做判斷,當有留言時,相當於 true,就會套用上 .has-discussion 這個 CSS class,呈現出小圓點;在留言區方面,我們用 {#if $$slots.commments} 做判斷,原理同上,當有留言時,相當於 true,就會顯示出留言區塊。

Slot Props

在第二集我們介紹過 Svelte 元件的 props,指的是元件對外暴露的變數,在調用元件時可以對 props 賦值,達到把值傳入元件的目的。

在 slot 層級,也有自己的 props,目的也是相仿的。

在下面的例子中,Hoverable 區塊內的「Hover over me!」是來自 App 元件給予的 slot 內容,而 Hoverable 元件本身一旦感應到游標,就會觸發變色,並且還要讓父元件 App 知道狀態發生變化,使 slot 字串變為「I am being hovered upon.」:

在 Hoverable 元件內,我們用一個 JS 變數 hovering 來判斷是否有游標在其上,再利用 hovering={hovering} 的聲明語句將 JS 變數 hoveringhovering 之名對外暴露。

在父元件 App 這邊,則是用 let:hovering={active} 的聲明語句將 hovering 重命名為 active,至此我們就可以在 App 同步接收到 Hoverable 元件內的狀態變化,並做出後續字串變更的判斷邏輯。

如果是具名的 slot,那它的 slot props 的用法會是這樣:

<span slot="name" let:hovering={active}></span>

小結

本集重點整理:

  • Svelte 元件可以提供一個或多個空位,稱為 slot,當元件被調用時,父元件可以把內容放入 slot 中
  • Slot 可以有預留內容,當此 slot 沒有被置入內容時,會顯示預留的內容
  • 元件內只有一個 slot 時,不必特地命名,但當一個元件內有提供多個 slot 時,必須為每個 slot 命名,才可正確對應
  • 元件內的 $$slots 變數是一個存放所有被傳入的 slot 內容的物件,我們可以根據它裡面某個 slot 的存在與否在程式碼做邏輯上的決斷
  • Slot 也有 props,slot props 將元件內的變數在 slot 的層級對外暴露,供應給父層元件調用

另外本集出現了一個新語句 let:,用於在父層元件調用子元件的 slot props。