Vue 2.6.0에 v-slot이라는 문법이 추가되었습니다.

기존 slot의 사용법을 간단하게 훝고, v-slot에 대해 알아보겠습니다.

slot

vue에서 slot은 자식컴포넌트의 엘리먼트를 부모에서 지정할 때 사용합니다.

// BaseButton.vue
<button>
  <slot>기본값</slot>
</button>
// Parent
<BaseButton>
  <!-- 슬롯이 들어갑니다. -->
  <span>저장하기</span>
</BaseButton>
// 렌더링 결과
<button>
  <span>저장하기</span>
</button>

위 코드처럼 BaseButtom 컴포넌트 템플릿 내에 엘리먼트를 사용하면 슬롯으로 지정됩니다.

컴포넌트에서 구체적인 요소를 선언하지 않고 부모에게 위임합니다.

이렇게 slot은 컴포넌트의 재사용성을 높여주는 중요한 기능입니다.

named-slot

named 슬롯은 slot을 사용할 때 슬롯을 적용할 엘리먼트를 지정할 때 사용됩니다.

자식 컴포넌트의 특정 영역을 지정해서 부모에서 사용 가능하게 합니다.

// BaseModal.vue
<div>
  <header>
    <slot name="header">
      <strong>기본 타이틀</strong>
      <button>기본 버튼</button>
    </slot>
  </header>
  <div>
    <slot name="content">
      <p>기본 콘텐츠</p>
    </slot>
  </div>
</div>
// Parent
<div>
  <BaseModal>
    <template slot="header">
      <h1>모달 제목</h1>
      <button>닫기</button>
    </template>
    <p slot="content">모달의 컨텐츠입니다.</p>
  </BaseModal>
</div>
// 렌더링 결과
<div>
  <header>
    <h1>모달 제목</h1>
    <button>닫기</button>
  </header>
  <div>
    <p>모달의 컨텐츠입니다.</p>
  </div>
</div>

위 예제처럼 named-slot을 사용하면 슬롯이 적용될 곳을 지정 가능하게 합니다.

그리고 header처럼 여러개의 엘리먼트는 template로 감싸서 사용할 수 있습니다.

slot-scope

slot 내에서 자식 컴포넌트의 데이터를 부모에서 사용하고 싶을 경우 slot-scope을 사용하면 됩니다.

// BaseModal.vue
<template>
  <div>
    <header>
      <slot name="header" :hello="childData" :close="close">
        <strong>기본 타이틀</strong>
        <button>기본 버튼</button>
      </slot>
    </header>
    <div>
      <slot name="content">
        <p>기본 콘텐츠</p>
      </slot>
    </div>
  </div>
</template>

<script>
export default {
  name: "BaseModal",
  data() {
    return {
      childData: "hello",
      active: false
    };
  },
  methods: {
    close() {
      this.active = false
    }
  }
}
</script>
// Parent
<div>
  <BaseModal>
    <template slot="header" slot-scope="slotProps">
      <h1>모달 제목</h1>
      <button @click="slotProps.close">닫기</button>
      {{ slotProps }} <!-- { hello: 'hello' } -->
    </template>
    <p slot="content">모달의 컨텐츠입니다.</p>
  </BaseModal>
</div>

slot-scope를 이용해서 마치 부모에서 자식으로 props를 넘겨주듯이 사용하면 됩니다.

예시처럼 자식 컴포넌트의 이벤트를 부모에서 사용하는 것 또한 가능합니다.

v-slot

v-slot은 Vue 2.6에 추가된 슬롯 문법입니다.

간단하게 named-slot과 slot-scope를 합친 문법입니다.

In 2.6.0, we introduced a new unified syntax (the v-slot directive) for named and scoped slots. It replaces the slot and slot-scope attributes, which are now deprecated, but have not been removed and are still documented here. The rationale for introducing the new syntax is described in this RFC.

공식문서는 v-slot 사용을 지향하고 named-slotslot-scope 사용은 권장하지 않습니다.

지금까지 작성했던 BaseModal을 사용하는 Parent 코드를 다음과 같이 변경할 수 있습니다.

// Parent
<div>
  <BaseModal>
    <template v-slot:header="slotProps">
      <h1>모달 제목</h1>
      <button @click="slotProps.close">닫기</button>
      {{ slotProps }} <!-- { hello: 'hello' } -->
    </template>
    <template v-slot:content>
      <p>모달의 컨텐츠입니다.</p>
    </template>
  </BaseModal>
</div>

다만 주의사항이 있습니다.

위 코드 content에서 사용했듯이 v-slot은 반드시 <template>로 감싸서 사용해야합니다.

개인적으로는 불필요하다면 template를 사용안하는게 더 좋은데.. 바꾼 이유는 모르겠습니다.

// Parent
<template>
  <div>
    <BaseModal>
      <template #header="slotProps"> <!-- #으로 단축해서 사용 -->
        <h1>모달 제목</h1>
        <button @click="slotProps.close">닫기</button>
        {{ slotProps }} <!-- { hello: 'hello' } -->
      </template>
      <template #[slotName]> <!-- 동적인 슬롯명 사용 -->
        <p>모달의 컨텐츠입니다.</p>
      </template>
    </BaseModal>
  </div>
</template>

<script>
import BaseModal from '~/components/BaseModal.vue'
export default {
  components: {
    BaseModal
  },
  data() {
    return {
      slotName: 'content'
    }
  }
}
</script>

대신 v-bind(:)v-on(@)처럼#으로 단축해서 쓸 수 있습니다.

또한 동적인 컴포넌트 바인딩과 유사하게 slot 명을 변수로 만들어서 동적인 슬롯 사용도 가능합니다.

slot을 사용하면 컴포넌트를 더 잘 만들 수 있습니다.

멋진 컴포넌트 작성에 도움이 됐길 바랍니다.