슬롯

컴포넌트에 콘텐츠나 다른 컴포넌트를 또 다른 방식으로 주입시키는 데에 사용하는 방식. 일반적인 부모와 자식 컴포넌트 관계가 아니다. 자신에게 등록된 자식 컴포넌트의 내용을 재정의할 수 있는 방식이다.

마치 React.portal과 비슷한 방식이라 봐도 나쁘지는 않을듯. 완전히 같지는 않지만.

예를 들어 모달의 경우 정형화 된 형태가 있고, 이를 필요한 부분만 바꿔가면서 사용하기 위해 slot을 사용한다. 자식 컴포넌트에 포함되어 있는 slot 요소를 부모 컴포넌트에서 재정의하여 사용할 수 있다.

// SlotModalLayout.vue
// 자식 컴포넌트

<template>
  <div class="modal-container">
    <header>
      **<slot name="header"></slot>**
    </header>
    <main>
      **<slot></slot>**
    </main>
    <footer>
      **<slot name="footer"></slot>**
    </footer>
  </div>
</template>
// SlotModalUseLayout.vue

<template>
  <modal-layout>
    <template v-slot:**header**>
      <h1>팝업 타이틀</h1>
    </template>
    <template v-slot:**default**>
      <p>팝업 컨텐츠 1</p>
      <p>팝업 컨텐츠 2</p>
    </template>
    <template v-slot:**footer**>
      <button>닫기</button>      
    </template>
  </modal-layout>
</template>

<script>
import SlotModalLayout from './SlotModalLayout.vue'

export default {
  name: 'SlotModalLayout',
  components: {'modal-layout': SlotModalLayout}
}
</script>

<style scoped>

</style>

슬롯을 사용할 레이아웃을 만들 때, 부모 컴포넌트에서 임의로 바꿔치기 할 수 있는 부분을 slot 요소로 만들어 놓는다. 그리고 각 slot 요소에 이름 name을 지정하여 사용한다.

부모 컴포넌트에서는 v-slot:**name**으로 자식 컴포넌트에서 지정한 특정 슬롯에 데이터를 넣을 수 있다. 이 때 name이 지정되지 않았다면 default로 사용해주면 된다.

좀 더 깊은 설명

// Child.vue

<template>
    <div>
        <h3>Hello, From Child</h3>
    </div>
    <slot></slot>
</template>

<script>
    export default {

    }
</script>

<style lang="scss" scoped>
</style>
// App.vue
<template>
  <div id="app">Hello World</div>
  <Child>
    Hello From Slot
  </Child>  <!-- 자식 컴포넌트 사용 -->
</template>

<script>
import Child from '@/components/Child.vue'

export default {
  name: 'App',
  components: {  // 자식 컴포넌트를 등록하고
    Child
  },
}
</script>

화면에

Hello World
Hello, From Child
Hello From Slot

이런 순으로 적히게 된다.

범위 컴파일 : 슬롯 사이의 데이터는 부모 자식 컴포넌트 중 누구와 바인딩될까?

// App.vue
<template>
  <div id="app">Hello World</div>
  <Child>
    <button v-on:click="**pressed**">Press</button>
  </Child>  <!-- 자식 컴포넌트 사용 -->
</template>

<script>
import Child from '@/components/Child.vue'

export default {
  name: 'App',
  components: {  
    Child
  },
  methods: {
    pressed() {
      alert('hello from **parent**!')
    }
  }
}
</script>

버튼을 클릭하면 parent의 pressed method가 실행된다! 이는 아무리 자식 컴포넌트의 사이에 있는 데이터라 해도, 부모의 템플릿 안에 있다면 부모 템플릿 범위로 컴파일된다는 의미이다.

Scoped-slot : 자식 컴포넌트 안의 데이터를 부모 컴포넌트로 끄집어 내고 싶을 때

v-slot | Cracking Vue.js