進入第二個元件前,我們先介紹一下 Web Component 的第四個隱藏概念,也就是 slot(插槽)
。
不管是使用原生 JS 還是框架,當我們設計一個自訂元件時,通常不希望內容被固定死(設計套件也一樣),而是希望使用的人可以有彈性的應用。
而 slot
是 Web Component 提供的一種 插槽機制
,允許使用者在元件內部插入自訂內容。
如果把 template 想成元件的範本結構
,slot 就像是預留的空位
,讓使用者可以填入自己想要的內容,但不影響整體元件的封裝(給外部一個有限制但可自訂的入口)。
如果要用框架來說明,那麼在 Angular 就像 <ng-content>
, Vue 一樣是 slot
,React 則像是 props.children
。
不具名 slot
具名 Slot
在前面三大核心中有提到 template 可以用來定義一個可複製的 HTML 結構。
而當 slot 與 template 搭配時,我們就能設計一個可重複使用
、可插入不同內容
的元件!
今天先做一個小小的練習,試著用之前的 spinner 元件加入 slot 看看!
定義兩個 slot 區塊:
我們先在 label slot 先定義一個預設的內容:<span>Loading...</span>
。
spinner.js
class CatSpinner extends HTMLElement {
constructor() {
// JS 繼承機制
super();
const shadowRoot = this.attachShadow({ mode: 'open' }); // 加入 shadow DOM
const cloneNode = this.renderTemplate().content.cloneNode(true); // 複製 template.content
shadowRoot.appendChild(cloneNode); // 寫入 template
}
renderTemplate (){
const spinnerTemplate = document.createElement('template');
spinnerTemplate.innerHTML = `
<style>
.spinner-container {
display: flex;
align-items: center;
gap: 8px;
}
.spinner {
width: 24px;
height: 24px;
border: 3px solid #e5e5e5;
border-top-color: #4a32e3;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
<div class="spinner-container">
<!-- 預設的 spinner icon -->
<slot name="icon">
<div class="spinner"></div>
</slot>
<!-- 預設的 label -->
<slot name="label">
<span>Loading...</span>
</slot>
</div>
`;
return spinnerTemplate;
}
}
customElements.define('cat-spinner', CatSpinner);
將加入 slot 加入 html 中:
<body>
<div style="display: flex; align-items: center; padding: 20px">
<h2>Web Component</h2>
<!-- 預設 -->
<cat-spinner></cat-spinner>
<!-- 自訂 label -->
<cat-spinner>
<span slot="label">處理中,請稍候...</span>
</cat-spinner>
<!-- 自訂 icon + label -->
<cat-spinner>
<span slot="icon">🐱</span>
<span slot="label">Uploading...</span>
</cat-spinner>
</div>
</body>
依照上方程式碼,透過元件提供的 slot 區域,你可以傳入自定義的內容。
在使用 slot 時,盡量使用具名的 slot,使用者就算亂寫內容,也只能丟進指定的 slot 中,且會被預設樣式規範住。