Beomy

[Svelte] Slot

  9 mins read  

이번 포스트에서는 컴포넌트가 자식 요소를 가질 수 있도록 기능을 제공하는 slot을 살펴보도록 하겠습니다.

Slot

HTML 요소는 아래와 같이 자식 요소를 가질 수 있습니다.

<div>
  <p>I'm a child of the div</p>
</div>

컴포넌트도 slot을 사용하면 자식 요소를 가질 수 있게 됩니다. Vue.js와 Svelte의 slot 사용 방법은 비슷합니다.

Vue.js 예제 코드

Vue.js에서는 아래 코드와 같이 <slot>을 사용할 수 있습니다.

<!-- Box.vue -->
<template>
  <div class="box">
    <slot></slot>
  </div>
</template>
<!-- App.vue -->
<template>
  <Box>
    <h2>Hello!</h2>
    <p>This is a box. It can contain anything.</p>
  </Box>
</template>
<script>
  import Box from './Box'
  export default {
    components: {
      Box
    }
  }
</script>

Svelte 예제 코드

Svelte에서는 아래 코드와 같이 <slot>을 사용할 수 있습니다.

<!-- Box.svelte -->
<div class="box">
  <slot></slot>
</div>
<!-- App.svelte -->
<script>
  import Box from './Box.svelte';
</script>

<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything.</p>
</Box>

App.svelte<Box>의 자식 요소들이 Box.svelte<slot> 요소의 위치에 나타나게 됩니다.

Default slot

컴포넌트의 자식 요소가 정의되어 있지 않은 경우 기본 요소를 정의할 수 있습니다. Vue.js와 Svelte의 slot 기본값을 사용 방법은 비슷합니다.

Vue.js 예제 코드

Vue.js에서 slot의 기본값 사용 방법은 아래와 같습니다.

<!-- Box.vue -->
<template>
  <div class="box">
    <slot>
      <em>no content was provided</em>
    </slot>
  </div>
</template>
<!-- App.vue -->
<template>
  <Box>
    <h2>Hello!</h2>
    <p>This is a box. It can contain anything.</p>
  </Box>

  <Box/>
</template>
<script>
  import Box from './Box'
  export default {
    components: {
      Box
    }
  }
</script>

Svelte 예제 코드

Svelte에서 slot의 기본값 사용 방법은 아래와 같습니다.

<!-- Box.svelte -->
<div class="box">
  <slot>
    <em>no content was provided</em>
  </slot>
</div>
<script>
  import Box from './Box.svelte';
</script>
 
<Box>
  <h2>Hello!</h2>
  <p>This is a box. It can contain anything.</p>
</Box>

<Box/>

App.svelte<Box>의 자식 요소가 정의되어 있지 않을 경우, Box.svelte<slot> 태그 안에 요소가 화면에 출력됩니다.

Named slot

이름이 있는 slot을 만들 수 있습니다. 이름이 있는 slot 역시 Vue.js와 Svelte의 사용법이 비슷합니다.

Vue.js 예제 코드

Vue.js에서 아래 코드와 같이 이름이 있는 slot을 사용할 수 있습니다.

<!-- ContactCard.vue -->
<template>
  <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>
</template>
<!-- App.vue -->
<template>
  <ContactCard>
    <span slot="name">
      P. Sherman
    </span>

    <span slot="address">
      42 Wallaby Way<br>
      Sydney
    </span>
  </ContactCard>
</template>

<script>
  import ContactCard from './ContactCard.svelte';
  export default {
    components: {
      Box
    }
  }
</script>

Svelte 예제 코드

Svelte에서 아래 코드와 같이 이름이 있는 slot을 사용할 수 있습니다.

<!-- ContactCard.svelte -->
<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>
<!-- App.svelte -->
<script>
  import ContactCard from './ContactCard.svelte';
</script>

<ContactCard>
  <span slot="name">
    P. Sherman
  </span>

  <span slot="address">
    42 Wallaby Way<br>
    Sydney
  </span>
</ContactCard>

이름이 있는 Slot을 사용하면 원하는 위치에 요소를 배치할 수 있습니다. 위의 코드를 살펴보면,

  • ContactCard.svelte에는 <slot name="name">, <slot name="address">, <slot name="email"> 3개의 Slot이 정의되어 있습니다.
  • App.svelte에는 slot="name"slot="address"를 사용하여 ContactCard.svelte<slot name="name">, <slot name="address">가 정의된 위치에 나타납니다.
  • App.svelteslot="email"을 사용하지 않았기 때문에 ContactCard.svelte<slot name="email">은 기본 요소가 나타내게 됩니다.

Slot props

slot의 데이터를 상위 컴포넌트로 전달하는 방법을 살펴보겠습니다.

Vue.js 예제 코드

Vue.js에서는 아래와 같이 데이터를 전달할 수 있습니다.

<!-- Hoverable.vue -->
<template>
  <div @mouseenter="enter" @mouseleave="leave">
    <slot :hovering="hovering"></slot>
  </div>
</template>
<script>
  export default {
    data () {
      return {
        hovering: false
      }
    },
    methods: {
      enter () {
        this.hovering = true
      },
      leave () {
        this.hovering = false
      }
    }
  }
</script>
<!-- App.vue -->
<template>
  <div>
    <Hoverable>
      <template v-slot="slotProps">
        <div :class="{active : slotProps.hovering }">
          <p v-if="slotProps.hovering ">I am being hovered upon.</p>
          <p v-else>Hover over me!</p>
        </div>
      </template>
    </Hoverable>

    <Hoverable>
      <template v-slot="slotProps">
        <div :class="{active : slotProps.hovering }">
          <p v-if="slotProps.hovering ">I am being hovered upon.</p>
          <p v-else>Hover over me!</p>
        </div>
      </template>
    </Hoverable>
  </div>
</template>
<script>
  import Hoverable from 'Hoverable'

  export default {
    components: {
      Hoverable
    }
  }
</script>
<style>
  div {
    padding: 1em;
    margin: 0 0 1em 0;
    background-color: #eee;
  }

  .active {
    background-color: #ff3e00;
    color: white;
  }
</style>

Svelte 예제 코드

Svelte에서는 아래와 같이 데이터를 전달할 수 있습니다.

<!-- 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>
<!-- 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>
    {#if active}
      <p>I am being hovered upon.</p>
    {:else}
      <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

<Hoverable let:hovering={active}>
  <div class:active>
    {#if active}
      <p>I am being hovered upon.</p>
    {:else}
      <p>Hover over me!</p>
    {/if}
  </div>
</Hoverable>

Hoverable.svelte

Hoverable.svelte에 정의된 slot<slot hovering={hovering}></slot>와 같이 상위 컴포넌트로 전달할 값을 선언해 줍니다. hovering은 상위로 전달할 변수 명이고, {hovering}은 상위로 전달할 변수입니다. 두 개의 이름이 hovering으로 동일하기 때문에 <slot {hovering}></slot>와 같은 약어로 작성할 수 있습니다.

App.svelte

App.svelte<Hoverable let:hovering={active}>와 같이 let 디렉티브를 사용해서 값을 전달받을 수 있습니다. let:hoveringhoveringHoverable.svelte<slot hovering={...}><slot> 태그의 속성 이름, hovering과 동일해야 합니다.

이렇게 전달된 값은 로컬 방식으로 동작하기 때문에 별도의 변수를 정의하지 않아도 됩니다. 위의 예제의 active는 변수를 정의하지 않았습니다. 또한 active는 컴포넌트의 하위 요소에만 사용할 수 있습니다. 위의 예제에 두 개의 Hoverable 컴포넌트를 사용했는데, active는 각각의 컴포넌트 안에서 독립적으로 동작합니다.

참고