프론트엔드/vue

[vue2] slot

journey-dev 2023. 3. 26. 17:21

slot

  • 슬롯은 어떤 콘텐츠를 다른 컴포넌트에 전달할 수 있게 됨.
  • 활용 사례
    : 캐러셀을 만들때 부모 컴포넌트1과 2는 공통된 자식컴포넌트를 사용함.
      그러나 그 자식 컴포넌트의 레이아웃은 같지만 컨텐츠 내용만 바꿔 사용할 때 slot 활용할 수 있음.
  • 리액트에서의 outlet과 비슷하다고 생각됨.
  • 공식문서 : 영문 사이트 한글 사이트

✅ 기본사용 - 특정 컨텐츠를 전달 (단일 슬롯 사용)

  • Parent1과 Paren2는 Child컴포넌트를 공통으로 사용한다.
    그러나 컨텐트 내용은 어떤 부모 컴포넌트 인지에 따라 다르게 나타내야 하는 상황.
  • Parent에서 아무 컨텐츠도 쓰지 않았다면, 기본 값으로 slot에 정의해둔 컨텐츠인 "default" 가 나온다.
// Parent1.vue - "/home"
<template>
  <Child>
    <h3>home</h3>
  </Child>
</template> 

// Parent2.vue - "/detail"
<template>
  <Child>
    <h3>detail</h3>
  </Child>
</template> 

// Child.vue
<template>
  <div>
    <h1>title</h1>
    <slot>
      <h1>default hello</h1> // home, detail로 변경된다. 
    </slot>
  </div>
</template>

✅ 기본사용 - 컴포턴트 전체 컨텐츠를 전달 (단일 슬롯 사용)

  • slot에 들어갈 컨텐츠가 컴포넌트 내용 전체인 경우
    : 부모 컴포넌트에 v-slot 자리에 Child 컴포넌트 컨텐츠가 통으로 들어간다.
// Parent.vue
<template>
  <Child>
    <v-slot></v-slot>
  </Child>
</template>

// Child.vue
<template>
  <div>
    <h1>자식 컴포넌트 전체를 slot으로</h1>
    <p>1</p>
    <p>2</p>
    <p>3</p>
    <p>4</p>
  </div>
</template>

 


 네임드 슬롯 (여러개 슬롯 사용)

  • 부모 컴포넌트에서 아무 컨텐츠도 전달하지 않는다면, Child에 정의된 내용을 기본값으로 보여준다.
  • v-slot:header → (축약표현) #header
  • default slot은 네임 지정 안해도 된다.
// Parent1.vue
<template>
  <div>
    <h1>Parent1</h1>
    <Child>
      <template v-slot:header>
        <h1>Header - Parent111</h1>
      </template>

      <template v-slot:default>
        <h1>Default - Parent111</h1>
      </template>

      <template v-slot:footer>
        <h1>Footer - Parent111</h1>
      </template>
    </Child>
  </div>
</template>

// Parent2.vue
<template>
  <div>
    <h1>Parent2</h1>
    <Child>
      <template #header>
        <h1>Header - Parent222</h1>
      </template>

      <template #default>
        <h1>Default - Parent222</h1>
      </template>

      <template #footer>
        <h1>Footer - Parent222</h1>
      </template>
    </Child>
  </div>
</template>

// Child.vue
<template>
  <div class="container">
    <slot name="header">
      <h1>header - 부모에서 정의 된 컨텐츠가 없으면 기본값으로 이 내용 표출</h1>
    </slot>

    <!-- default slot은 네임 지정x -->
    <slot>
      <h1>기본값- 부모에서 정의 된 컨텐츠가 없으면 기본값으로 이 내용 표출</h1>
    </slot>

    <slot name="footer">
      <h1>footer - 부모에서 정의 된 컨텐츠가 없으면 기본값으로 이 내용 표출</h1>
    </slot>
  </div>
</template>

slot props (자식 slot에서 → 부모로 prop 값 보내기)

자식 컴포넌트에서만 접근할 수 있는 데이터를  부모 컴포넌트로 가져와야 할 경우 slot props 사용

 

  • 예제1. 자식 컴포넌트에서 → 부모 컴포넌트로 , 단순히 prop 넘기기
// Parent1.vue
<template>
  <div>
    <Child>
      <!-- [순서2]부모에서 prop을 받는다. // props = {"someProp":"something"} -->
      <template #header="props">
        <h1>{{ props.someProp }}</h1>
        <p>parent111</p>
      </template>
    </Child>
  </div>
</template>

// Parent2.vue
<template>
  <div>
    <Child>
      <!-- [순서2]부모에서 prop을 받는다. (구조분해할당)  -->
      <template #header="{ someProp }">
        <h1>{{ someProp }}</h1>
        <p>parent222</p>
      </template>
    </Child>
  </div>
</template>

// Child.vue
<template>
  <div class="container">
    <!-- [순서1] 자식 컴포넌트 slot에서 prop을 넘겨준다. -->
    <slot name="header" :someProp="some"> </slot>
    <p>slot에서 prop을 넘겨준다.</p>
  </div>
</template>
<script>
export default {
  name: "ChildView",
  data() {
    return {
      some: "something",
    };
  },
};
</script>

출력 결과

  • 예제2. 자식의 slot내용에서 → 부모로 “이벤트 객체”를 prop으로 넘기기
// Parent.vue
 <template>
  <div>
    <Child>
      <!-- [순서2]부모에서 prop을 받는다.
           축약임. 원랜 {on : {mouseenter: mouseenter(), mouseleave :mouseleave () }}-->
      <template #header="{ on }">
        <button v-on="on">버튼</button>
      </template>
    </Child>
  </div>
</template>

// Child.vue
<template>
  <div class="container">
    <!-- [순서1] 자식 컴포넌트 slot에서 prop을 넘겨준다. -->
    <slot name="header" :on="{ mouseenter, mouseleave }"> </slot>
    <p>slot에서 prop을 넘겨준다.</p>
  </div>
</template>

<script>
export default {
  name: "ChildView",
  methods: {
    mouseenter() {
      console.log("mouseenter!!");
    },
    mouseleave() {
      console.log("mouseleave~");
    },
  },
};
</script>

※ 이벤트 바인딩 v-on

<h1 v-on:click="dothisFn"> </h1>
<h1 @click="dothisFn"> </h1>  // 축약

// 여러 이벤트 타입을 등록할 떄
<h1 v-on="{click: dothisFn,  mousedown: mouseFn}"> </h1>

 

  • 예제3 툴팁 만들기 (이벤트객체 관련)
    : 토글 여부를 자식요소의 데이터로 관리하고 있음
      그 데이터를 컨트롤 하는 이벤트 핸들러를 slot prop으로 부모로 전달,
     이후 부모 컴포넌트에서 이벤트 발생되고 > 자식 컴포넌트의 데이터가 바뀜에 따라 토글 내용이 slot으로 들어오고 빠지게됨.
// Parent.vue
<template>
  <div>
    <h1>Parent</h1>
    <Child>
      <!-- [순서2] 자식으로 부터 prop을 받아서 버튼에 걸어줌, 부모에서 자식 데이터 제어  
             축약 {on : { click: showDialog }}  -->
      <template v-slot:activator="{ on }">
        <v-btn v-on="on">show</v-btn>
      </template>

      <template v-slot:showContent>
        <h3>토글 내용</h3>
      </template>
    </Child>
  </div>
</template>

// Child.vue
<template>
  <div class="container">
    <h1>Child</h1>
    <!-- [순서1]부모 컴포넌트로, click이벤트시 자식 컴포넌트의 data 바꾸는 함수를 prop으로 보내줌 -->
    <slot name="activator" :on="{ click: showDialog }"> </slot>

    <!-- [순서3] show가 true일 경우에만 보여지는 slot -->
    <slot name="showContent" v-if="show"> </slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      show: false,
    };
  },
  methods: {
    showDialog() {
      this.show = !this.show;
    },
  },
};
</script>

출력 화면