2020. 10. 29. 15:27ㆍ재주껏 하는 Front-End/Svelte (준비중)
이번 글에서는 Svelte의 Slot에 대해 알아보자. Slot이란 특정 컴포넌트에 전달할 요소 또는 자식 컴포넌트를 주입할 때 사용하는 것으로 Vue에서 Slot과 동일하다. 예제를 통해 기본적인 사용 방법에 대해 알아보자.
1) App.svelte
<script>
import Box from './Box.svelte';
</script>
<Box>
<!-- put content here -->
<p>
Hello World !!
</p>
</Box>
2) Box.svelte
<style>
.box {
width: 300px;
border: 1px solid #aaa;
border-radius: 2px;
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
padding: 1em;
margin: 0 0 1em 0;
}
</style>
<div class="box">
<!-- content should be injected here -->
<slot></slot>
</div>
실행 결과는 아래와 같다.
위의 예제와 같이 Box 컴포넌트에 <p> 태그를 전달하면 Box 컴포넌트 내에 있는 <slot> 태그 위치에 전달된 요소가 주입된다.
Slot Failbacks
때로는 Slot이 제대로 들어오지 못하는 경우가 있을 수 있다. 이 경우에는 <slot> 태그 사이에 요소 또는 컴포넌트를 넣어 Slot이 안 들어왔을 경우에 출력하도록 기본 값을 설정할 수 있다. 위의 예제를 아래와 같이 변경하자.
1) App.svelte
<script>
import Box from './Box.svelte';
</script>
<Box></Box>
2) Box.svelte
<style>
.box {
width: 300px;
border: 1px solid #aaa;
border-radius: 2px;
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
padding: 1em;
margin: 0 0 1em 0;
}
</style>
<div class="box">
<slot>
<p style="color: red; font-size: 2em">
Slot이 없습니다.
</p>
</slot>
</div>
출력 결과는 아래와 같다.
Named Slots
하나의 컴포넌트에 여러 개의 Slot을 전달해야 하는 경우, 여러 개의 Slot이 적합한 위치에 삽입되도록 하려면 Slot에 이름을 붙이면 된다. 슬롯을 전달받을 컴포넌트 내부의 <slot> 태그에 name 속성으로 이름을 부여한 후, 상위 컴포넌트에서 요소 및 컴포넌트를 전달할 때 slot 속성에 부여한 이름을 맞춰서 사용하면 된다.
1. 슬롯을 전달받는 부분
...
<slot name="슬롯 이름"></slot>
...
2. 슬롯을 전달하는 부분
<parentComponent>
<div slot="슬롯 이름">
...
</div>
</parentComponent>
사용 예제 코드를 살펴보자.
1) ContactCard.svelte
<style>
.contact-card {
width: 300px;
border: 1px solid #aaa;
border-radius: 2px;
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
padding: 1em;
}
h2 {
padding: 0 0 0.2em 0;
margin: 0 0 1em 0;
border-bottom: 1px solid #ff3e00
}
.address, .email {
padding: 0 0 0 1.5em;
background: 0 50% no-repeat;
background-size: 1em 1em;
margin: 0 0 0.5em 0;
line-height: 1.2;
}
.address { background-image: url(tutorial/icons/map-marker.svg) }
.email { background-image: url(tutorial/icons/email.svg) }
.missing { color: #999 }
</style>
<article class="contact-card">
<h2>
<slot name="name">
<span class="missing">Unknown name</span>
</slot>
</h2>
<div class="address">
<slot name="address">
<span class="missing">Unknown address</span>
</slot>
</div>
<div class="email">
<slot name="email">
<span class="missing">Unknown email</span>
</slot>
</div>
</article>
2) App.svelte
<script>
import ContactCard from './ContactCard.svelte';
</script>
<ContactCard>
<span slot="name">Kim1124</span>
<span slot="address">한국</span>
<span slot="email">example@example.com</span>
</ContactCard>
ContactCard 컴포넌트 내부에 <slot name="...">로 Slot의 이름을 정의한 후, 상위 컴포넌트인 App.svelte에서 ContactCard 컴포넌트를 추가한 부분에 <span slot="...">으로 들어갈 Slot을 지정하는 것을 확인할 수 있다. 실행 결과는 아래와 같다.
$$slots
새롭게 추가된 $$slots은 상위 컴포넌트에서 특정 슬롯의 이름을 사용했는지에 대한 여부를 Boolean 값으로 리턴한다. 예를 들어, 상위 컴포넌트에서 Slot을 전달할 때 <div slot="slot1">로 되어있다면, $$slots.slot1에 true 값을 전달한다. 만약, 상위 컴포넌트에서 이름 Slot을 사용하지 않았다면 undefined가 출력된다.
$$slots는 이름을 가진 Slot이 사용되었는지에 대한 여부만 판단하기 때문에 하위 컴포넌트에서 Slot을 정의해놓고 상위에서 사용하지 않은 경우 렌더링을 시키지 않는 방법으로 성능 최적화를 할 때 사용할 수 있다. 아래의 예제 코드를 살펴보자.
1) Card.svelte
<div>
<slot name="title"></slot>
<span> {$$slots.description}</span>
{#if $$slots.description}
<!-- This slot and the <hr> before it will not render. -->
<hr>
<slot name="description"></slot>
{/if}
</div>
2) App.svelte
<script>
import Card from './Card.svelte'
</script>
<Card>
<h1 slot="title">$$Slot 사용 예제</h1>
<span slot="description">상위 컴포넌트에서 전달한 description 슬롯 ...</span>
</Card>
만약, 상위 컴포넌트에서 description이라는 Slot을 사용하지 않는다면 하위 컴포넌트의 description Slot은 렌더링 되지 않는다. 실행 결과는 아래와 같다.
Slot Props
Slot Props는 <slot> 태그가 명시된 하위 컴포넌트의 값을 상위 컴포넌트로 전달할 때 사용한다. 전달된 값은 반드시 Slot을 사용하는 컴포넌트 내에서만 사용할 수 있기 때문에 데이터 흐름을 복잡하게 하지 않고 상, 하위 컴포넌트가 편하게 데이터를 공유할 수 있다는 장점이 있다. Slot Props의 기본 사용 방법은 아래와 같다.
1. 하위 컴포넌트
<script>
let slotProps = 값
</script>
<slot slotProps={slotProps}></slot>
2. 상위 컴포넌트
<Component let:slotProps={slotPropsValue}>
<div>
{{ slotPropsValue }}
</div>
</Component>
하위 컴포넌트에서 슬롯에 Props를 전달하면 상위 컴포넌트에서는 let 키워드로 slotProps를 전달받은 뒤 컴포넌트 내부에서만 사용하면 된다. 아래의 예제 코드로 실제 사용 코드를 살펴보자.
1) Hoverable.svelte
<script>
let hovering;
function enter() {
hovering = true;
}
function leave() {
hovering = false;
}
</script>
<div on:mouseenter={enter} on:mouseleave={leave}>
<slot hovering={hovering}></slot>
</div>
2) App.svelte
<script>
import Hoverable from './Hoverable.svelte';
</script>
<style>
div {
padding: 1em;
margin: 0 0 1em 0;
background-color: #eee;
}
.active {
background-color: #ff3e00;
color: white;
}
</style>
<Hoverable let:hovering={active}>
<div class:active={active}>
{#if active}
<p>I am being hovered upon.</p>
{:else}
<p>Hover over me!</p>
{/if}
</div>
</Hoverable>
하위 컴포넌트인 Hoverable의 <slot> 태그에 hovering이라는 Props를 전달하고, 상위 컴포넌트인 App.svelte에서 let 키워드를 사용하여 전달한 Props 값을 사용한다. 실행 결과는 아래와 같다.
다음 글에서는 Svelte에서 지원하는 특수한 요소에 대해 알아보도록 하겠다.
'재주껏 하는 Front-End > Svelte (준비중)' 카테고리의 다른 글
12. Context API (2) | 2020.11.24 |
---|---|
11. Special Elements (0) | 2020.11.12 |
9. 스토어 (0) | 2020.10.07 |
8. 컴포넌트 생명 주기 (0) | 2020.09.22 |
7. 바인딩 (0) | 2020.09.14 |