IT/언어

[Vue.js] slot에 대해 이해하기

개발자 두더지 2023. 4. 18. 22:37
728x90

일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.

 

slot이란?


 slot이란 부모가 되는 컴포넌트쪽에서 자식 컴포넌트의 템플렛의 일부를 삽입하는 기능이다. 참고로, Vue.js Version2.5 이후는 slot-scope, Version 2.6이후는 slot-scope와 v-slot이 준비되어 있다. 

 slot은 크게 기본 slot, 이름이 붙은 slot, 스코프가 있는 slot 세 개로 나눠서 볼 수 있다. 

 

 

기본 slot


 /src/components 바로 아래에 매우 간단한 컴포넌트를 작성한다.

<template>
  <div class="mycom">
  <p>name: Mirai Taro<p>
  </div>
</template>
<style>
</style>

 /src/views/About.vue에는 만든 컴포넌트를 호출한다.

 아래의 코드에서는 <MyCom></MyCom>의 태그로 컴포넌트를 이용한다. 여기서 <MyCome> 태그로 감싼 부분이 무시되는 것을 확인해보길 바란다. 무시되는 것을 테스트해보기 위해 "未来太郎"라고 기재했다.

<template>
  <div class="home">
    <MyCom>test</MyCom>
  </div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
  components: {
    MyCom
  }
}
</script>

 결과를 보면 알 수 있듯, About.vue상에서 작성한 "未来太郎"는 무시되어 출력되지 않는다.

 

slot

컴포넌트쪽의 템플릿에 <slot> 태그를 기재하면 그 장소에는 슬롯 컨텐츠가 포함된다.

<template>
  <div class="mycom">
   <p>name:<slot>Mirai Taro</slot></p>
  </div>
</template>
<style>
</style>

 부모쪽에서 슬롯 컨텐츠가 정의되어 있는 경우는 <slot>태그로 감싼 컴포넌트쪽의 컨텐츠는 표시되지 않고, 부모 쪽의 슬롯 컨텐츠가 표시된다. 이번 예에서는 About.vue상의 "未来太郎"가 표시되는 것을 확인해보자.

 출력 결과는 다음과 같다.

 

 

이름이 붙은 slot


 여러 개의 slot을 이용하고 싶은 경우에는 다른 이름을 붙여서 이용한다. Vue 2.6.0이후에는 컴포넌트쪽에 <slot name = "XXX"> 과 같은 형식으로 이름 붙은 slot을 정의할 수 있다.

<template>
  <div class="mycom">
    <p>name:<slot name="nm">Mirai Taro</slot></p>
    <p>address:<slot name="add">Osaki</slot></p>
  </div>
</template>
<style>
</style>

 부모쪽에서는 v-slot:XXX의 형식으로 컴포넌트의 태그 내에 삽입하고 싶은 이름이 붙은 slot을 지정할 수 있다.

<template>
  <div class="home">
    <MyCom></MyCom>
    <MyCom>
      <template v-slot:nm>未来太郎</template>
    </MyCom>
    <MyCom>
      <template v-slot:add>渋谷</template>
    </MyCom>
  </div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
  components: {
    MyCom
  }
}
</script>

 위 코드에서는 1행째는 컴포넌트대로 출력, 2행째는 이름이 부모 쪽의 컨텐츠에 2행째는 주소가 부모의 컨텐츠로 바뀌어 있는 것을 확인할 수 있다.

 

이름이 붙은 slot의 생략기법

 v-bind를 ":", v-on을 "@"으로 생략할 수 있듯, v-slot도 "#"으로 생략하여 기재할 수 있다. 방금 봤던 About.vue의 코드를 다음과 같이 작성할 수 있다는 것이다.

<template>
  <div class="home">
    <MyCom></MyCom>
    <MyCom>
      <template #nm>未来太郎</template>
    </MyCom>
    <MyCom>
      <template #add>渋谷</template>
    </MyCom>
  </div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
  components: {
    MyCom
  }
}
</script>

 

 

스코프가 있는 slot


 스코프가 있는 slot을 이용하여 자식 컴포넌트에서 부모 컴포넌트에 대해서 슬롯 컨텐츠의 정의에 필요한 데이터를 주고받을 수 있다. 

 참고로 자식에서 부모로 데이터를 전달하고 싶을 때 Vuex를 사용하는 쪽이 좋은 경우가 많지만, 이번에는 slot 설명을 위해 이 방법을 사용하고 있는 것을 알린다.

 

제대로 작동하지 않는 경우

아래와 같은 컴포넌트를 준비한다.

<template>
  <div class="mycom">
    <p>name:<slot>{{ userNm.enName }}</slot></p>
  </div>
</template>
<script>
export default {
  name: 'MyCom',
  data () {
    return {
      userNm: {
        enName: 'Mirai Taro',
        jpName: '未来太郎' // ←slot内で参照したいデータ
      }
    }
  }
}
</script>
<style>
</style>

 이때 다음과 같이 jpName을 부모쪽에서 호출할 수 없다. 

<template>
  <div class="home">
    <MyCom>
      {{ userNm.jpName }}
    </MyCom>
  </div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
  components: {
    MyCom
  }
}
</script>

 

스코프가 있는 slot을 기재 방법

 스코프가 있는 slot을 이용할 때는 자식 컴포넌트쪽에서는 <slot>태그에 대해서 v-bind를 한다.

<template>
  <div class="mycom">
    <p>name:<slot :userNm="userNm">{{ useNm.enName }}</slot></p>
  </div>
</template>
<script>
export default {
  name: 'MyCom',
  data () {
    return {
      userNm: {
        enName: 'Mirai Taro',
        jpName: '未来太郎' // ←slot내에서 참조하고 싶은 데이터
      }
    }
  }
}
</script>
<style>
</style>

 부모쪽에는 <v-slot:default> 로 받는 것으로 자식 컴포넌트의 jpName의 값을 획득할 수 있다. <v-slot:default="slotProps">의 slotProps는 임의이므로 중복되지 않으면 어떤 문자열도 상관없다.

<template>
  <div class="home">
    <MyCom v-slot:default="slotProps">
      {{ slotProps.userNm.jpName }}
    </MyCom>
  </div>
</template>
<script>
import MyCom from '../components/MyCom.vue'
export default {
  components: {
    MyCom
  }
}
</script>

 또한, 이름이 붙은 slot를 이용하는 경우, default의 부분이 각각의 slot의 이름이 된다. 


참고자료

https://future-architect.github.io/articles/20200428/

https://blog.amayz.co.jp/?p=983

728x90