🖥️프론트엔드/Vue3

[Vue 3] Slot은 왜 사용하는가?

meteorfish 2025. 6. 18. 16:19
728x90

 

기존 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하기 위해선 아래와 같은 과정이 진행된다.

// 부모 컴포넌트
// Bank.vue
<Account :money="money" />

// 자식 컴포넌트
// Account.vue
<script>
	export default {
    	name : 'Account',
        props : {
        	money : String,
        }
    }
</script>

1. 부모 컴포넌트에서 커스텀 태그 및 v-bind를 통해 데이터 넘겨주기

2. 자식 컴포넌트에서 props로 바인딩된 데이터 가져오기

3. 자식 컴포넌트에서 사용하기 (위 코드에선 생략됨)

 

이 과정을 보다 편하게 사용할 수 있도록 Slot 태그를 사용할 수 있다.

 

BaseCard.vue

<template>
    <div>
        <slot></slot>
    </div>
</template>

<style scoped>
div {
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
  padding: 1rem;
  margin: 2rem auto;
  max-width: 40rem;
}
</style>

div 태그를 가지는 컴포넌트로 재사용을 위해 전역 컴포넌트로 등록하여 사용하고 있다.

이 곳에 다양한 값들을 slot 태그를 통해 넣을 수 있다.

 

<template>
    <li>
        <base-card>
            <header>
                <h3>{{ title }}</h3>
                <base-button mode="flat" @click="deleteResource(id)">Delete</base-button>
            </header>
            <p>{{ description }}</p>
            <nav>
                <a :href="link">View Resource</a>
            </nav>
        </base-card>
    </li>
</template>

<script>
export default {
    props: ['id', 'title', 'description', 'link'],
    inject: ['deleteResource']
}
</script>

<style scoped>
// 생략
</style>

<base-card> 태그를 통해 BaseCard 컴포넌트의 slot 태그로 태그들을 넘길 수 있다.

 

이름이 있는 슬롯

만약 하나의 컴포넌트의 슬롯이 여러 개 있는 경우에는 이름을 부여하여 한다.

예를 들어 두 개의 슬롯이 있을 경우, 하나의 슬롯에만 이름을 부여하면 나머지 슬롯은 default라는 이름이 자동으로 부여된다.

 

BaseDialog

<template>
  <teleport to="body">
    <div @click="$emit('close')"></div>
    <dialog open>
        <header>
            <slot name="header">
                <h2>{{ title }}</h2>
            </slot>
        </header>
        <section>
            <slot></slot>
        </section>
        <menu>
            <slot name="actions">
                <base-button @click="$emit('close')">Close</base-button>
            </slot>
        </menu>
    </dialog>
  </teleport>
</template>

<script>
export default {
    props: {
        title: {
            type: String,
            required: true,
        }
    },
    emits: ['close']
}
</script>

<style scoped>
// 생략
</style>

이 코드의 경우 header, actions, default 총 3개의 슬롯이 사용되고 있다.

 

<template>
    <base-dialog v-if="inputIsInvalid" title="Invalid Input" @close="confirmError">
        <template #default>
            <p>Unfortunately, at least one input value is invalid.</p>
            <p>Please check all inputs and make sure you enter at least a few character.</p>
        </template>
        <template #actions>
            <base-button @click="confirmError">Okay</base-button>
        </template>
    </base-dialog>
    <base-card>
        <form @submit.prevent="submitData">
            <div class="form-control">
                <label for="title">Title</label>
                <input id="title" name="title" type="text" ref="titleInput"/>
            </div>
            <div class="form-control">
                <label for="description">Description</label>
                <textarea id="description" name="description" rows="3" ref="descInput"></textarea>
            </div>
            <div class="form-control">
                <label for="link">Link</label>
                <input id="link" name="link" type="url" ref="linkInput"/>
            </div>
            <div>
                <base-button type="submit">Add Resource</base-button>
            </div>
        </form>
    </base-card>
</template>

base-dialog라는 태그 하위에 template 태그를 통해 slot을 지정할 수 있다.

`#슬롯이름` 으로 지정이 가능하다.

 

 

728x90