這是〈Svelte 從小白到入門〉系列的第十二集,各集連結如下:
- 〈Svelte 從小白到入門(一)元件、事件、Reactivity〉
- 〈Svelte 從小白到入門(二)Props、邏輯、迴圈、Await〉
- 〈Svelte 從小白到入門(三)事件〉
- 〈Svelte 從小白到入門(四)數據綁定〉
- 〈Svelte 從小白到入門(五)生命週期〉
- 〈Svelte 從小白到入門(六)Store〉
- 〈Svelte 從小白到入門(七)動態效果〉
- 〈Svelte 從小白到入門(八)進出場效果〉
- 〈Svelte 從小白到入門(九)動畫效果〉
- 〈Svelte 從小白到入門(十)Action〉
- 〈Svelte 從小白到入門(十一)CSS Class〉
- 〈Svelte 從小白到入門(十二)Slot〉
- 〈Svelte 從小白到入門(十三)Context〉
- 〈Svelte 從小白到入門(十四)Svelte 的特殊元素〉
- 〈Svelte 從小白到入門(十五)Module Context〉
- 〈Svelte 從小白到入門(十六)Debugging〉
- 〈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 變數 hovering
以 hovering
之名對外暴露。
在父元件 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。