cover

팝업창과 같이 전체적인 UI는 유사하지만 내용이 다르게 들어가고싶은 경우가 있습니다. 컴포넌트에 slot을 만들고 v-slot 디렉티브를 이용하면 되는데요. 컴포넌트 UI 내에 원하는 부분은 유동적으로 바꿀 수 있습니다.

v-slot 미리보기

v-slot의 두 가지 기능

v-slot은 두 가지 기능을 통합한 디렉티브입니다.

Named slot : 하위 컴포넌트에 동적으로 템플릿, 태그 추가하는 기능
Scoped slot : 하위 컴포넌트의 값을 상위 컴포넌트에서 사용할 수 있게 하는 기능

그러면 이 두 기능이 어떤 것인지 알아야 하겠죠?

Named slot

컴포넌트를 사용할 때 특정 영역은 상위 컴포넌트에서 태그들을 넣을 수 있도록 하는 기능입니다.

slot

slot이라는 태그를 사용하는데요. 이 부분에 들어갈 태그 내용은 상위 컴포넌트가 정하도록 하겠다는 의미입니다.

vue
child.vue
      <div class="child">
	<h2> Child </h2>
	
	<main>
		<slot></slot>
	</main>
</div>
    

상위 컴포넌트에서는 태그 사이에 원하는 태그들을 넣어주면 됩니다.

vue
parent.vue
      <div class="parent">
	<h1> Parent </h1>
	<Child>
		<h3> main의 제목 </h3>
		<p> main의 내용 </p>
	</Child>
</div>
    

그러면 다음과 같이 렌더링 됩니다.

html
결과
      <div class="parent">
	<h1> Parent </h1>
	<div class="child">
		<h2> Child </h2>
		
		<main>
			<h3> main의 제목 </h3>
			<p> main의 내용 </p>
		</main>
	</div>
</div>
    

왜 이렇게 나오는지 이해가 안되실 수도 있을거 같아서 그림으로도 만들어봤습니다.

named slot 원리
named slot 원리

slot에 대한 이해가 가시나요?

slot 여러 개 사용하기

위의 예시 경우 하나의 slot을 만들었지만 추가로 여러 개 만들고 싶을 수도 있습니다. 그런 경우에는 명칭을 붙여줘서 구분을 해야합니다. 이를 named slot이라 합니다.

html
      <slot name="명칭"></slot>
    

Child에 header, footer slot을 추가해보겠습니다.

vue
Child.vue
      <div class="child">
	<header>
		<slot name="header"></slot>
	</header>

	<main>
		<slot></slot> <!-- default -->
	</main>

	<footer>
		<slot name="footer"></slot>
	</footer>
</div>
    
info
      slot에 이름을 넣지 않은 경우는 name이 default가 됩니다.
    

이전에는 단순히 하위 컴포넌트 안에 내용만 넣으면 됐지만 여러 개인 경우 template태그와 v-slot을 사용해야합니다.

html
      <template v-slot:명칭></template>
    

Parent에 적용해보면

vue
Parent.vue
      <div class="parent">
	<Child>
		<template v-slot:header>
			<h1> header slot 영역 </h1>
		</template>
		
		<!-- <template v-slot> 과 동일-->
		<template v-slot:default>
			<p> default slot 영역 </div>
		</template>
		
		<template v-slot:footer>
			<p> footer slot 영역 </div>
		</template>
	</Child>
</div>
    

v-slot 약식

v-bind(:)나 v-on(@)처럼 v-slot도 #으로 줄여쓸 수 있습니다.

html
      <template #header></template>
    

v-slot dynamic slot name

v-slot에 넣을 이름도 동적으로 정하고 싶다면 괄호[]를 사용하면 됩니다.

html
      <template v-slot:[컴포넌트 변수]></template>
<template #[컴포넌트 변수]></template>
    

v-slot 넣지 않은 경우 처리

하위 컴포넌트에서 slot을 만들어놨지만 상위 컴포넌트에서 해당 slot을 무시할 수도 있습니다. 그럴 경우 하위 컴포넌트에서 처리하는 두 가지 방법이 있습니다.

1. slot에 기본값을 넣어주는 방법입니다.
html
      <slot>슬롯을 채우지 않을 경우 이 글이 나타납니다.</slot>
    
1. $slots 값을 통해 조건부 렌더링하기
html
      <main v-if="$slots.default">
	<slot></slot>
</main>

<footer v-show="$slots.footer">
	<slot name="footer"></slot>
</footer>
    

Scoped slot

상위 컴포넌트에서 하위 컴포넌트의 값을 가져와 렌더링에 사용할 수 있습니다.

scoped slot 문법

하위 컴포넌트에서 name 외 속성으로 상위 컴포넌트에서 값을 보내줄 수 있습니다.

html
      <slot 속성="값"></slot>
    
html
      <template v-slot="변수">
	<div> {{ 변수.값 }} </div>
</template>
    

scoped slot 예시

vue
Child.vue
      <template>
	<div class="child">
		<slot name="header" message="hello" :user="user"></slot>
	</div>
</template>

<script>
export default {
	data() {
		return {
			user: "vuelog"
		};
	},
}
</script>
    

하위 컴포넌트에서 header slot에 messageuser 라는 값을 올려보내고자 합니다.

vue
Parent.vue
      <template>
	<div class="parent">
		<template v-slot:header="headerProps">
			<div> {{ headerProps.message }} </div>
			<div> {{ headerProps.user }} </div>
		</template>
	</div>
</template>
    

header slot에서 사용할 값들은 headerProps에 담아서 messageuser 값들을 가져와서 사용합니다. (headerProps는 임의로 지은 변수명입니다)

scoped slot 여러 개

named slot 과 마찬가지로 slot이 여러 개인 경우도 동일하게 사용하면 됩니다.

vue
Child.vue
      <div class="child">
	<header>
		<slot name="header" title="Title"></slot>
	</header>

	<main>
		<slot :content="content"></slot> 
	</main>

	<footer>
		<slot name="footer" :message="message"></slot>
	</footer>
</div>
    
vue
Parent.vue
      <div class="parent">
	<Child>
		<template v-slot:header="headerProps">
			<h1> {{ headerProps.title }} </h1>
		</template>
		
		<!-- <template v-slot> 과 동일-->
		<template v-slot:default="mainProps">
			<p> {{ mainProps.content }} </div>
		</template>
		
		<template v-slot:footer="footerProps">
			<p>{{ footerProps.message }}</div>
		</template>
	</Child>
</div>
    

scoped slot destructuring

매번 headerProps, mainProps, footerProps 이렇게 매번 변수명을 지어야하는 건 귀찮은 일이죠. ES6 문법인 구조 분해 문법(destructuring)을 사용하면 됩니다.

html
      <template v-slot:default="{ content }"></template>
    

마무리하며

v-slotnamed slot, scoped slot 기능이 합쳐진 것입니다.

image

다양한 예제를 보며 복습해보겠습니다.

html
      <!-- named slot default -->
<template v-slot></template>
<template v-slot:default></template>

<!-- dynamic named slot -->
<template v-slot:[value]></template>

<!-- named slot 약식 -->
<template #header></template>

<!-- scoped slot -->
<template v-slot:footer="footerProps">{{ footerProps.msg }}</template>

<!-- scoped slot destructuring -->
<template v-slot:default="{ msg }">{{ msg }}</template>

<!-- 응용 -->
<template #[name]="{ msg }">{{ msg }}</template>