컴포넌트 태그 사이에 정보를 넣어, 자식 컴포넌트로 컨텐츠를 전달할 수 있다.
<!-- App.vue -->
<template>
<Mybtn>Apple</Mybtn>
<Mybtn>Banana</Mybtn>
<Mybtn>Cherry</Mybtn>
</template>
<script>
import Mybtn from '~/components/MyBtn'
export default {
components: {
Mybtn
}
}
</script>
부모 컴포넌트의 컨텐츠를 자식 컴포넌트에 <slot></slot>
슬롯 태그가 선언된 위치에 받을 수 있다.
<!-- MyBtn.vue -->
<template>
<button type="button" class="btn">
<slot></slot>
</button>
</template>
<!-- 렌더된 화면 -->
<button type="button" class="btn">Apple</button>
<button type="button" class="btn">Banana</button>
<button type="button" class="btn">Cherry</button>
부모 컴포넌트에서 컨텐츠가 넘어오지 않는다면 아래와 같이 <slot></slot>
태그 사이에 기본값을 넣을 수 있다.
<!-- MyBtn.vue -->
<template>
<button type="button" class="btn">
<slot>버튼</slot>
</button>
</template>
아래와 같이 html 태그
및 다른 컴포넌트
도 슬롯 내용으로 넣을 수 있다.
<!-- App.vue -->
<template>
<Mybtn>Apple</Mybtn>
<Mybtn>
<Icon></Icon>
<span>btn2</span>
</Mybtn>
<Mybtn>Cherry</Mybtn>
</template>
<script>
import Icon from '~/components/Icon'
import Mybtn from '~/components/MyBtn'
export default {
components: {
Icon,
Mybtn
}
}
</script>
부모 템플릿에 있는 모든 요소는 부모 스코프에서 컴파일되고,
자식 템플릿에 있는 모든 요소는 자식 스코프에서 컴파일된다.
아래와 같이 데이터 바인딩도 가능하다.
<template>
<Mybtn>Apple</Mybtn>
<Mybtn>Banana {{ msg }}</Mybtn>
<Mybtn>Cherry</Mybtn>
</template>
<script>
import Mybtn from '~/components/MyBtn'
export default {
components: {
Mybtn
},
data() {
return {
msg: 'msg??'
}
}
}
</script>
<!-- App.vue -->
<template>
<Layout>
<template v-slot:header>
<div class="header">
헤더에 들어갈 내용입니다.<br>
페이지마다 중복되는 헤더는 컴포넌트로 들어가도 되겠네요.
</div>
</template>
<template v-slot:main>
<div class="main">메인에 들어갈 내용입니다.</div>
</template>
<template v-slot:footer>
<div class="footer">
푸터에 들어갈 내용입니다.<br>
페이지마다 중복되는 푸터는 컴포넌트로 들어가도 되겠네요.
</div>
</template>
</Layout>
</template>
<script>
import Layout from '~/components/Layout'
export default {
components: {
Layout
}
}
</script>
아래와 같이 v-slot:
대신 축약형 #
으로 사용할 수도 있다.
슬롯의 이름을 지정해주지 않으면 축약형을 사용할 수 없다.
ex ) <template #default>
는 되지만 <template #>
는 안된다.
<template #header>
<!-- Layout.vue -->
<template>
<div class="wrap">
<slot name="header"></slot>
<slot name="main"></slot>
<slot name="footer"></slot>
</div>
</template>
<!-- 렌더된 화면 -->
<div class="wrap">
<div class="header">
헤더에 들어갈 내용입니다.<br>
페이지마다 중복되는 헤더는 컴포넌트로 들어가도 되겠네요.
</div>
<div class="main">메인에 들어갈 내용입니다.</div>
<div class="footer">
푸터에 들어갈 내용입니다.<br>
페이지마다 중복되는 푸터는 컴포넌트로 들어가도 되겠네요.
</div>
</div>
페이지마다 중복되는 헤더와 푸터 컴포넌트를 슬롯의 기본값으로 넣어놓으면 아래와 같이, 여러 페이지 작업시 깔끔하고 가독성 높게 컨텐츠 부분만 집중해서 작성 가능하다.
<!-- App.vue -->
<template>
<Layout>
<template v-slot:main>
<div class="main">메인에 들어갈 내용입니다.</div>
</template>
</Layout>
</template>
<!-- Layout.vue -->
<template>
<div class="wrap">
<slot name="header">
<Header></Header>
</slot>
<slot name="main"></slot>
<slot name="footer">
<Footer></Footer>
</slot>
</div>
</template>
header
또는 footer
컴포넌트에 데이터를 전달하고 싶을 때가 있다. 그러면 아래와 같이 슬롯에 들어갈 템플릿을 선언해주면 그만이다.
이렇게 작성하면 헤더,푸터를 공통으로 쓰고 있는 페이지와 그렇지 않은 페이지에 대한 구분을 쉽게 할 수 있어 좋을거 같다.
<!-- App.vue -->
<template>
<Layout>
<template v-slot:header>
<Header>props 등을 전달하여 커스텀 또는 slot 활용</Header>
</template>
<template v-slot:main>
<div class="main">메인에 들어갈 내용입니다.</div>
</template>
<template v-slot:footer>
<Footer>props 등을 전달하여 커스텀 또는 slot 활용</Footer>
</template>
</Layout>
</template>
<script>
import Layout from '~/components/Layout'
import Header from '~/components/Header'
import Footer from '~/components/Footer'
export default {
components: {
Layout,
Header,
Footer
}
}
</script>
자식 컴포넌트 슬롯의 데이터를 부모 쪽에서 제어하고 싶을 때가 있다.
<slot>
태그에 데이터를 바인딩 하여 부모 쪽으로 전달할 수 있다.
이 때 전달된 데이터는 v-slot:default
의 값
으로, 마치 매개변수
처럼 들어오게 되는데, 변수명은 어떤 것이든 상관 없다. (아래의 slotProps)
<!-- App.vue -->
<template>
<List>
<template v-slot:default="slotProps">
<span class="icon"></span>
<a href="#none">{{ slotProps.childData }}</a>
</template>
</List>
</template>
<script>
import List from '~/components/List'
export default {
components: {
List
}
}
</script>
<!-- List.vue -->
<template>
<ul>
<li v-for="item in items" :key="item">
<slot :childData="item"></slot>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: ['효율적인 슬롯 활용', '편리한 슬롯 활용']
}
}
}
</script>
아래와 같이 ES6 문법을 활용하여 객체 접근 단계를 줄일 수 있다.
<!-- App.vue -->
<template>
<List>
<template v-slot:default="{ childData }">
<span class="icon"></span>
<a href="#none">{{ childData }}</a>
</template>
</List>
</template>
단일 슬롯을 사용할 경우
아래와 같이 컴포넌트 태그가 슬롯의 템플릿을 대신할 수 있다.
<!-- App.vue -->
<template>
<List v-slot:default="{ childData }">
<span class="icon"></span>
<a href="#none">{{ childData }}</a>
</List>
</template>
ES6 문법으로 들어오는 데이터명을 임의로 변경 가능하다.
<!-- App.vue -->
<template>
<List v-slot:default="{ childData: text }">
<span class="icon"></span>
<a href="#none">{{ text }}</a>
</List>
</template>
값이 undefined로 넘어올 경우 대체할 기본값도 설정할 수 있다.(마찬가지로 ES6 문법)
<!-- App.vue -->
<template>
<List v-slot:default="{ childData = '데이터가 없습니다.' }">
<span class="icon"></span>
<a href="#none">{{ childData }}</a>
</List>
</template>
동적으로 슬롯 이름을 바뀌어야 될 경우는 거의 없긴 하지만, 같은 구조가 케이스에 따라 html 구조는 같되 위치만 바뀌는 경우 동적으로 슬롯 네임을 변경해주면 활용하기 좋다.
<!-- child.vue -->
<template>
<div class="wrap">
<slot name="main">
<div>메인 슬롯 기본값</div>
</slot>
<div style="margin:20px 0">중간 다른 요소</div>
<slot name="etc">
<div>etc 슬롯 기본값</div>
</slot>
</div>
</template>
동적인 슬롯 이름을 사용하여 데이터 변경에 따라 슬롯 위치를 제어할 수 있다.
<template>
<Child>
<template v-slot:[slotName]>
<div>------ 동적인 슬롯 ------</div>
</template>
</Child>
</template>
<script>
import Child from '~/components/Child'
export default {
components: { Child },
data() {
return {
slotName: 'main'
}
}
}
</script>