10. Slot

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