[Vue3] Render Scope 과 Scoped Slot 활용


:raising_hand: Vue 개발 중 공부내용을 기록하는 포스트 입니다.


image

1. 개요


Slot 을 사용하는데 있어서 데이터 접근 범위와 이를 활용하는 Scoped Slot 에 대해 이전 포스팅에 이어서 작성해본다.

2. Render Scope


이전 포스팅에서 이어서 Slot 을 사용하는데 있어서 데이터의 접근 범위는 어떻게 될것인가?

예를들어서 아래와 같은 소스가 있다고 생각해보자.

<template>
    <TheSlot>
        <template #header> 헤더 </template>
        <template #footer> 푸터</template>
        <template #default>디폴트</template>
    </TheSlot>
</template>
<script setup>
  import Theslot from './TheSlot.vue'
  const templateMessage = ref('템플릿 메시지 입니다.')
</script>

// Child Component
<template>
  <div>
    <slot name="header"></slot>
  </div>
  <div>
    <slot></slot>
  </div>
  <div>
    <slot name="footer"></slot>
  </div>
</template>

<script setup>
  const headerMessgae = ref('헤더 메시지 입니다.')
</script>

이와 같은 구조의 Slot 을 활용하기에 있어서 Rendering ScopetemplateMessgae 만 해당될 것이다.

이유는 Slot 콘텐츠인 templateMessage 는 상위 ParentComonent 에서는 활용이 가능하지만 자식 컴포넌트에 정의된 headerMessgae 는 접근할 수 없다는 것이다.

이를 해결할 수 있는 방법이 Scoped Slot 이다.

Scoped Slots


아래 소스를 통해 이해해보자.

// Parent Component

<template>
    <TheSlot>
      <!-- Child Component 에서 전달한 데이터를 상위 컴포넌트에서 사용가능 -->
        <!-- 구조분해를 통해 slotProps 생략가능 -->
        <template #header="{ headerMessage }">  </template>
        <template #footer="slotProps"> </template>
        <template #default>디폴트</template>
    </TheSlot>
</template>
</template>
<script setup>
  const templateMessage = ref('템플릿 메시지 입니다.')
</script>

// Child Component
<template>
  <div>
    <slot name="header" :header-message="headerMessage"></slot>
  </div>
  <div>
    <slot></slot>
  </div>
  <div>
    <slot name="footer" :footer-message="footerMessage"></slot>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const headerMessage = ref('헤더 메시지 입니다.')
const footerMessage = ref('푸터 메시지 입니다.')
</script>

<style lang="scss" scoped></style>

주석에서 보앗듯이, 자식 컴포넌트에서 전달한 값들을 상위 컴포넌트에서 받아서 사용할 수 있다.

이를 통해 좀 더 유연하고 넓은 범위의 데이터를 활용할 수 있을 것이다.

원하는 Named Slot 만 사용하기


<template>
    <TheSlot>
        그냥 사용
    </TheSlot>
</template>
<script setup>
  import Theslot from './TheSlot.vue'
  const templateMessage = ref('템플릿 메시지 입니다.')
</script>

// Child Component
<template>
  <div>
    <slot name="header"></slot>
  </div>
  <div>
    <slot></slot>
  </div>
  <div>
    <slot name="footer"></slot>
  </div>
</template>

<script setup>
  const headerMessgae = ref('헤더 메시지 입니다.')
</script>

위와 같이 사용했을때 이상한 점이 있다.

우리는 그냥 TheSlot 에 컨텐츠를 넣었을 뿐인데 TheSlot 에 정의된 모든 Slotdiv 가 생성되는 것을 볼 수 있다.

이럴때 우리가 원하는 Slotdiv 만 생성할 수 있는 방법이 있다.

<template>
    <TheSlot>
        그냥 사용
    </TheSlot>
</template>
<script setup>
  import Theslot from './TheSlot.vue'
  const templateMessage = ref('템플릿 메시지 입니다.')
</script>

// Child Component
<template>
  <div v-if="hasSlots">
    <slot name="header"></slot>
  </div>
  <div v-if="$slots.default">
    <slot></slot>
  </div>
  <div v-if="$slots.footer">
    <slot name="footer"></slot>
  </div>
</template>

<script setup>
import { ref, useSlots, computed } from 'vue'

const headerMessage = ref('헤더 메시지 입니다.')
const footerMessage = ref('푸터 메시지 입니다.')

const slots = useSlots()
const hasSlots = computed(() => !!slots.header)
</script>

위와 같이 자식 컴포넌트에 Rendering 조건 을 명시하게 되면 실제로 div 가 렌더링되지 않게 할 수 있다.

또한 위와 같이 useSlotscomputed 를 활용해서 간단하게 원하는 div slot 만 나올 수 있도록 할 수 있다.