Slot

jude·2022년 2월 12일
0

vue

목록 보기
8/14
post-thumbnail

Slot

기본 사용법

컴포넌트 태그 사이에 정보를 넣어, 자식 컴포넌트로 컨텐츠를 전달할 수 있다.

<!-- App.vue -->
<template>
  <Mybtn>Apple</Mybtn>
  <Mybtn>Banana</Mybtn>
  <Mybtn>Cherry</Mybtn>
</template>

<script>
import Mybtn from '~/components/MyBtn'

export default {
  components: {
    Mybtn
  }
}
</script>

부모 컴포넌트의 컨텐츠를 자식 컴포넌트에 <slot></slot> 슬롯 태그가 선언된 위치에 받을 수 있다.

<!-- MyBtn.vue -->
<template>
  <button type="button" class="btn">
    <slot></slot>
  </button>
</template>
<!-- 렌더된 화면 -->
<button type="button" class="btn">Apple</button>
<button type="button" class="btn">Banana</button>
<button type="button" class="btn">Cherry</button>

기본 대체값 (Fallback)

부모 컴포넌트에서 컨텐츠가 넘어오지 않는다면 아래와 같이 <slot></slot> 태그 사이에 기본값을 넣을 수 있다.

<!-- MyBtn.vue -->
<template>
  <button type="button" class="btn">
    <slot>버튼</slot>
  </button>
</template>

slot 구성 요소

아래와 같이 html 태그 및 다른 컴포넌트도 슬롯 내용으로 넣을 수 있다.

<!-- App.vue -->
<template>
  <Mybtn>Apple</Mybtn>
  <Mybtn>
    <Icon></Icon>
    <span>btn2</span>
  </Mybtn>
  <Mybtn>Cherry</Mybtn>
</template>

<script>
import Icon from '~/components/Icon'
import Mybtn from '~/components/MyBtn'

export default {
  components: {
    Icon,
    Mybtn
  }
}
</script>

렌더 스코프

부모 템플릿에 있는 모든 요소는 부모 스코프에서 컴파일되고,
자식 템플릿에 있는 모든 요소는 자식 스코프에서 컴파일된다.

아래와 같이 데이터 바인딩도 가능하다.

<template>
  <Mybtn>Apple</Mybtn>
  <Mybtn>Banana {{ msg }}</Mybtn>
  <Mybtn>Cherry</Mybtn>
</template>

<script>
import Mybtn from '~/components/MyBtn'

export default {
  components: {
    Mybtn
  },
  data() {
    return {
      msg: 'msg??'
    }
  }
}
</script>

이름이 있는 Slot

기본 사용법

<!-- App.vue -->
<template>
  <Layout>
    <template v-slot:header>
      <div class="header">
        헤더에 들어갈 내용입니다.<br>
        페이지마다 중복되는 헤더는 컴포넌트로 들어가도 되겠네요.
      </div>
    </template>
    
    <template v-slot:main>
      <div class="main">메인에 들어갈 내용입니다.</div>
    </template>

    <template v-slot:footer>
      <div class="footer">
        푸터에 들어갈 내용입니다.<br>
        페이지마다 중복되는 푸터는 컴포넌트로 들어가도 되겠네요.
      </div>
    </template>
  </Layout>
</template>

<script>
import Layout from '~/components/Layout'

export default {
  components: {
    Layout
  }
}
</script>

아래와 같이 v-slot: 대신 축약형 # 으로 사용할 수도 있다.
슬롯의 이름을 지정해주지 않으면 축약형을 사용할 수 없다.

ex ) <template #default>는 되지만 <template #>는 안된다.

<template #header>
<!-- Layout.vue -->
<template>
  <div class="wrap">
    <slot name="header"></slot>

    <slot name="main"></slot>
    
    <slot name="footer"></slot>
  </div>
</template>
<!-- 렌더된 화면 -->
<div class="wrap">
  <div class="header">
    헤더에 들어갈 내용입니다.<br>
    페이지마다 중복되는 헤더는 컴포넌트로 들어가도 되겠네요.
  </div>
  
  <div class="main">메인에 들어갈 내용입니다.</div>
  
  <div class="footer">
    푸터에 들어갈 내용입니다.<br>
    페이지마다 중복되는 푸터는 컴포넌트로 들어가도 되겠네요.
  </div>
</div>

레이아웃 컴포넌트의 슬롯 활용

페이지마다 중복되는 헤더와 푸터 컴포넌트를 슬롯의 기본값으로 넣어놓으면 아래와 같이, 여러 페이지 작업시 깔끔하고 가독성 높게 컨텐츠 부분만 집중해서 작성 가능하다.

<!-- App.vue -->
<template>
  <Layout>
    <template v-slot:main>
      <div class="main">메인에 들어갈 내용입니다.</div>
    </template>
  </Layout>
</template>
<!-- Layout.vue -->
<template>
  <div class="wrap">
    <slot name="header">
      <Header></Header>
    </slot>

    <slot name="main"></slot>
    
    <slot name="footer">
      <Footer></Footer>
    </slot>
  </div>
</template>

header 또는 footer 컴포넌트에 데이터를 전달하고 싶을 때가 있다. 그러면 아래와 같이 슬롯에 들어갈 템플릿을 선언해주면 그만이다.

이렇게 작성하면 헤더,푸터를 공통으로 쓰고 있는 페이지그렇지 않은 페이지에 대한 구분을 쉽게 할 수 있어 좋을거 같다.

<!-- App.vue -->
<template>
  <Layout>
    <template v-slot:header>
      <Header>props 등을 전달하여 커스텀 또는 slot 활용</Header>
    </template>

    <template v-slot:main>
      <div class="main">메인에 들어갈 내용입니다.</div>
    </template>

    <template v-slot:footer>
      <Footer>props 등을 전달하여 커스텀 또는 slot 활용</Footer>
    </template>
  </Layout>
</template>

<script>
import Layout from '~/components/Layout'
import Header from '~/components/Header'
import Footer from '~/components/Footer'

export default {
  components: {
    Layout,
    Header,
    Footer
  }
}
</script>

스코프를 갖는 Slot

자식 컴포넌트 슬롯의 데이터를 부모 쪽에서 제어하고 싶을 때가 있다.
<slot> 태그에 데이터를 바인딩 하여 부모 쪽으로 전달할 수 있다.
이 때 전달된 데이터는 v-slot:default으로, 마치 매개변수처럼 들어오게 되는데, 변수명은 어떤 것이든 상관 없다. (아래의 slotProps)

<!-- App.vue -->
<template>
  <List>
    <template v-slot:default="slotProps">
      <span class="icon"></span>
      <a href="#none">{{ slotProps.childData }}</a>
    </template>
  </List>
</template>

<script>
import List from '~/components/List'

export default {
  components: {
    List
  }
}
</script>
<!-- List.vue -->
<template>
  <ul>
    <li v-for="item in items" :key="item">
      <slot :childData="item"></slot>
    </li>
  </ul>
</template>

<script>
export default {
  data() {
    return {
      items: ['효율적인 슬롯 활용', '편리한 슬롯 활용']
    }
  }
}
</script>

아래와 같이 ES6 문법을 활용하여 객체 접근 단계를 줄일 수 있다.

<!-- App.vue -->
<template>
  <List>
    <template v-slot:default="{ childData }">
      <span class="icon"></span>
      <a href="#none">{{ childData }}</a>
    </template>
  </List>
</template>

단일 슬롯을 사용할 경우 아래와 같이 컴포넌트 태그가 슬롯의 템플릿을 대신할 수 있다.

<!-- App.vue -->
<template>
  <List v-slot:default="{ childData }">
    <span class="icon"></span>
    <a href="#none">{{ childData }}</a>
  </List>
</template>

ES6 문법으로 들어오는 데이터명을 임의로 변경 가능하다.

<!-- App.vue -->
<template>
  <List v-slot:default="{ childData: text }">
    <span class="icon"></span>
    <a href="#none">{{ text }}</a>
  </List>
</template>

값이 undefined로 넘어올 경우 대체할 기본값도 설정할 수 있다.(마찬가지로 ES6 문법)

<!-- App.vue -->
<template>
  <List v-slot:default="{ childData = '데이터가 없습니다.' }">
    <span class="icon"></span>
    <a href="#none">{{ childData }}</a>
  </List>
</template>

동적인 이름을 갖는 Slot

동적으로 슬롯 이름을 바뀌어야 될 경우는 거의 없긴 하지만, 같은 구조가 케이스에 따라 html 구조는 같되 위치만 바뀌는 경우 동적으로 슬롯 네임을 변경해주면 활용하기 좋다.

<!-- child.vue -->
<template>
  <div class="wrap">
    <slot name="main">
      <div>메인 슬롯 기본값</div>
    </slot>
    
    <div style="margin:20px 0">중간 다른 요소</div>
    
    <slot name="etc">
      <div>etc 슬롯 기본값</div>
    </slot>
  </div>
</template>

동적인 슬롯 이름을 사용하여 데이터 변경에 따라 슬롯 위치를 제어할 수 있다.

<template>
  <Child>
    <template v-slot:[slotName]>
      <div>------ 동적인 슬롯 ------</div>
    </template>
  </Child>
</template>

<script>
import Child from '~/components/Child'
export default {
  components: { Child },
  data() {
    return {
      slotName: 'main'
    }
  }
}
</script>
profile
UI 화면 만드는걸 좋아하는 UI개발자입니다. 프론트엔드 개발 공부 중입니다. 공부한 부분을 블로그로 간략히 정리하는 편입니다.

0개의 댓글