Scoped Slot을 이용해 하위 컴포넌트의 데이터에 접근하는 방법을 알아봅니다.
이전 시리즈
2022.12.21 - [Vue.js] - Vue3 컴포넌트 디자인 패턴 소개
2022.12.22 - [Vue.js] - Vue3 컴포넌트 디자인 패턴 : Props
2022.12.22 - [분류 전체보기] - Vue3 컴포넌트 디자인 패턴 : Slots
Scoped Slots 소개
Scoped Slots이 뭐죠?
다음과 같이 v-slot 디렉티브에 속성을 할당하면, 자식의 데이터를 부모에서 접근할 수 있습니다.
Scoped Slots 사용하기
📄LogoHeader.vue
<template>
<slot name="header" />
</template>
<script>
export default {
data() {
return {
logoImage: '/images/logo.png'
}
}
}
</script>
📄LogoHeader.vue
<template>
<slot name="header" :logo="logoImage" />
</template>
<script>
export default {
data() {
return {
logoImage: '/images/logo.png'
}
}
}
</script>
📄LogoHeader.vue
<template>
<slot name="header" :logo="logoImage" />
</template>
<script>
export default {
data() {
return {
logoImage: '/images/logo.png'
}
}
}
</script>
📄App.vue
<template>
<LogoHeader>
<template v-slot:header="slotProps">
{{ slotProps.logo }}
</template>
</LogoHeader>
</template>
다시 한번 DIY
해당 흐름은 은근히 이해가 잘 안갑니다.
1단계. 자식 컴포넌트에서 원하는 슬롯에 데이터를 prop으로 노출
📄Book.vue
<template>
<div class="book">
<slot name="title" :bookTitle="bookTitle"></slot>
</div>
</template>
<script>
export default {
data() {
return {
bookTitle: 'Child Providing Data'
}
}
}
</script>
2단계. 부모 컴포넌트에서 <template> 블록에 slot prop 노출
📄Library.vue
<template>
<Book>
<template v-slot:title="slotProps">
<!-- How do we get the bookTitle from Book.vue? -->
</template>
</Book>
</template>
3단계. 부모 컴포넌트에서 <template> 블록에서 원하는 slot prop 사용
📄Library.vue
<template>
<Book>
<template v-slot:title="slotProps">
{{ slotProps.bookTitle }}
</template>
</Book>
</template>
제한사항과 모범사례
왜 Scoped Slot인가요?
v-slot 디렉티브를 적용한 슬롯으로 prop 적용 범위가 한정되기 때문입니다.
📄Book.vue
<template>
<div class="book">
<slot name="title" :bookTitle="bookTitle" />
</div>
</template>
<script>
export default {
data() {
return {
bookTitle: 'Child Providing Data'
}
}
}
</script>
📄Library.vue
<template>
<Book>
<template v-slot:title="slotProps">
<h1>{{ slotProps.bookTitle }}</h1>
</template>
</Book>
</template>
slotProps를 통해 노출된 bookTitle 속성에 대한 일반적인 오해는
상위 컴포넌트의 method, computed 또는 기타 위치에서 사용할 수 있다는 것입니다.
📄Library.vue
<template>
<Book>
<template v-slot:title="slotProps">
<h1>{{ slotProps.bookTitle }}</h1>
</template>
</Book>
</template>
<script>
export default {
computed: {
uppercaseTitle() {
// 🛑THIS DOES NOT WORK
this.slotProps.bookTitle.toUpperCase()
}
}
}
</script>
📄Book.vue
<template>
<div class="book">
<slot name="title"
:bookTitle="bookTitle"
:uppercaseBookTitle="uppercaseTitle"
/>
</div>
</template>
<script>
export default {
data() {
return {
bookTitle: 'Child Providing Data'
}
},
computed: {
uppercaseTitle() {
return this.bookTitle.toUpperCase()
}
}
}
</script>
Slot Props 구조분해
📄Library.vue (before)
<template>
<Book>
<template v-slot:title="slotProps">
<h1>{{ slotProps.bookTitle }}</h1>
</template>
</Book>
</template>
📄Library.vue (after)
<template>
<Book>
<template v-slot:title="{ bookTitle }">
<h1>{{ bookTitle }}</h1>
</template>
</Book>
</template>
단일 v-slot 축약 문법
위에서 v-slot은 템플릿 요소에만 적용해야 한다고 했으나 사실 거짓말입니다😅.
기술적으로 예외가 있으며 지금부터 살펴보겠습니다.
📄Library.vue (before v-slot abbreviated shorthand)
<template>
<Book>
<template v-slot:title="{ bookTitle }">
<h1>{{ bookTitle }}</h1>
</template>
</Book>
</template>
📄Library.vue (with v-slot abbreviated shorthand)
<template>
<Book v-slot:title="{ bookTitle }">
<h1>{{ bookTitle }}</h1>
</Book>
</template>
📄Library.vue (with multiple slots and incorrect syntax)
<template>
<Book v-slot:title="{ bookTitle }">
<h1>{{ bookTitle }}</h1>
<!-- 🛑THIS DOES NOT WORK -->
<template v-slot:description>
<p>My Description</p>
</template>
</Book>
</template>
📄Library.vue (with multiple slots)
<template>
<Book>
<template v-slot:title="{ bookTitle }">
<h1>{{ bookTitle }}</h1>
</template>
<template v-slot:description>
<p>My Description</p>
</template>
</Book>
</template>
그래서 저는 그냥 항상 탬플릿 블록을 사용하는 것을 권장합니다.
참고
이와 같이 scoped slot을 사용하는 패턴을 렌더리스 컴포넌트 패턴이라 한다.
https://adamwathan.me/renderless-components-in-vuejs/
'FrontEnd' 카테고리의 다른 글
Vue3 컴포넌트 디자인 패턴 : v-model (0) | 2022.12.22 |
---|---|
Vue3 컴포넌트 디자인 패턴 : Object binding (0) | 2022.12.22 |
Vue3 컴포넌트 디자인 패턴 : Slots (0) | 2022.12.22 |
Vue3 컴포넌트 디자인 패턴 : Props (0) | 2022.12.22 |
Vue3 컴포넌트 디자인 패턴 소개 (0) | 2022.12.21 |