Vue Components - Slots

YEZI🎐·2022년 11월 30일
0

Vue

목록 보기
20/45

Slots

<child-component>
  {{ message }}
</child-component>

다음과 같은 템플릿이 있다고 가정했을 때
message는 부모 데이터에 바인딩 된다.

컴포넌트 범위에 대한 간단한 법칙 :
상위 템플릿의 모든 내용은 상위 범위로 컴파일되며,
하위 템플릿의 모든 내용은 하위 범위에서 컴파일된다.

범위 컴파일

일반적인 실수는 부모 템플릿의 하위 속성/메소드에 디렉티브를 바인딩하려고 하는 것이다.

<!-- someChildProperty가 자식 컴포넌트의 속성이라고 가정하면 작동하지 않음 -->
<child-component v-show="someChildProperty"></child-component>

상위 템플릿은 하위 컴포넌트의 상태를 인식하지 못한다.
컴포넌트 루트 노드에서 하위 범위 디렉티브를 바인딩 해야하는 경우,
하위 컴포넌트의 자체 템플릿에서 하위 범위 디렉티브를 바인딩해야 한다.

Vue.component('child-component', {
  // 올바른 위치에 놓여 있으며 이제 작동함
  template: '<div v-show="someChildProperty">Child</div>',
  data: function () {
    return {
      someChildProperty: true
    }
  }
})

단일 슬롯

하위 컴포넌트 템플릿에 최소한 하나의 <slot> 콘텐츠가 포함되어 있지 않으면 부모 콘텐츠가 삭제된다.
속성이 없는 슬롯이 하나뿐인 경우 전체 내용 조각이 DOM의 해당 위치에 삽입되어 슬롯 자체를 대체한다.
원래 <slot> 태그 안에 있는 내용은 대체 콘텐츠로 간주 되며,
대체 콘텐츠는 하위 범위에서 컴파일되며 호스팅 엘리먼트가 비어있고 삽입할 콘텐츠가 없는 경우에만 표시된다.

<!-- 자식 컴포넌트 -->
<div>
  <h2>ㅎㅇ 나 자식 컴포넌트 제목</h2>
  <slot>
    제공된 컨텐츠가 없는 경우에만 볼 수 있음
  </slot>
</div>
<!-- 부모 컴포넌트 -->
<div>
  <h1>ㅎㅇ 난 부모 컴포넌트 제목</h1>
  <my-component>
    <p>이건 원본 컨텐츠이고</p>		<!-- 부모에서 삽입 콘텐츠 -->
    <p>요건 원본 중 추가 컨텐츠임</p>	<!-- 부모에서 삽입 콘텐츠 -->
  </my-component>
</div>
<!-- 결과 화면 -->
<div>
  <h1>ㅎㅇ 난 부모 컴포넌트 제목</h1>
  <div>
    <h2>ㅎㅇ 나 자식 컴포넌트 제목</h2>
    <p>이건 원본 컨텐츠이고</p>
    <p>요건 원본 중 추가 컨텐츠임</p>
  </div>
</div>

Named Slots

<slot> 엘리먼트는 특별한 속성인 name을 가지고 있다.
이 속성은 어떻게 내용을 배포해야 하는 지를 커스터마이징 하는 데 사용한다.
이름이 다른 슬롯이 여러 개 있을 수 있으며,
이름을 가진 슬롯은 부모 컴포넌트에서 slot 속성이 있는 엘리먼트의 slot 속성 값과 일치해야 한다.

명명되지 않은 슬롯이 하나 있을 경우 :
이름이 없는 기본 슬롯은 일치하지 않는 콘텐츠의 포괄적인 콘텐츠 역할을 하며
기본 슬롯이 없으면 일치하지 않는 콘텐츠가 삭제된다.

<!-- 자식 컴포넌트 -->
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
<!-- 부모 컴포넌트 -->
<app-layout>
  <h1 slot="header">여기에 페이지 제목이 있을거임</h1>

  <p>메인 컨텐츠 단락</p>
  <p>하나 더 있음</p>

  <p slot="footer">연락처 정보</p>
  <!-- ↑ 같은 거 ↓ -->
  <template v-slot:footer>
    <p>연락처 정보</p>
  </template>
</app-layout>
<!-- 결과 화면 -->
<div class="container">
  <header>
    <h1>여기에 페이지 제목이 있을거임</h1>
  </header>
  <main>
    <p>메인 컨텐츠 단락</p>
    <p>하나 더 있음</p>
  </main>
  <footer>
    <p>연락처 정보</p>
  </footer>
</div>

범위를 가지는 슬롯(Scope Slot)

범위가 지정된 슬롯은 이미 렌더링 된 엘리먼트 대신 재사용 가능한 템플릿(데이터를 전달할 수 있음)으로 작동하는 특별한 유형의 슬롯이다.
props를 컴포넌트에게 전달하는 것처럼, 하위 컴포넌트에서 단순히 데이터를 슬롯에 전달하면 된다.

<!-- 자식 컴포넌트 -->
<div class="child">
  <slot text="hello from child"></slot>
</div>
<!-- 부모 컴포넌트 -->
<div class="parent">
  <child>
    <template slot-scope="props">
      <span>hello from parent</span>
      <span>{{ props.text }}</span>
    </template>
  </child>
</div>
<!-- 결과 화면 -->
<div class="parent">
  <div class="child">
    <span>hello from parent</span>
    <span>hello from child</span>
  </div>
</div>

범위가 지정된 슬롯의 일반적인 사용 사례는 컴포넌트 사용자가 리스트의 각 항목을 렌더링 하는 방법을 사용자 정의할 수 있는 리스트 컴포넌트이다.

<my-list :items="items">
  <!-- scoped slot 역시 이름을 가질 수 있음 -->
  <li
    slot="item"
    slot-scope="props"
    class="my-fancy-item">
    {{ props.text }}
  </li>
</my-list>
<ul>
  <slot name="item"
    v-for="item in items"
    :text="item.text">
    <!-- 대체 컨텐츠는 여기 -->
  </slot>
</ul>

디스트럭처링

slot-scope 값은 실제로 함수 서명의 인수 위치에 나타날 수 있는 유효한 JavaScript 표현식이며,
지원되는 환경(싱글 파일 컴포넌트 또는 최신 브라우저)에서 ES2015 디스트럭처링을 사용할 수 있다.

<child>
  <span slot-scope="{ text }">{{ text }}</span>
</child>

Named Slots - v-slot

Vue 2.5 버전에서 스콥드 슬롯을 사용할 때 slot-scope 속성을 <template>태그에만 사용하는 것이 아닌, 기존 태그에 사용할 수 있도록 허용한 것이 문제가 되었다.
(자세한 내용은 스콥드 슬롯의 문제점에서 확인)
때문에 v-slot디렉티브가 등장하게 된다.

v-slot 디렉티브는 <template>에 사용할 수 도 있으며, v-slot<template> 태그에 추가할 수 있다.

<!-- 부모 컴포넌트 -->
<app-layout>
  <template v-slot:header>
    <h1>여기에 페이지 제목이 있을거임</h1>
  </template>

  <p>메인 컨텐츠 단락</p>
  <p>하나 더 있음</p>
  <!-- ↑ 같은 거 ↓ -->
  <template v-slot:default>
    <p>메인 컨텐츠 단락</p>
    <p>하나 더 있음</p>
  </template>

  <template v-slot:footer>
    <p>연락처 정보</p>
  </template>
</app-layout>

범위를 가지는 슬롯 - v-slot

부모 컴포넌트의 슬록에서 user를 쓰기 위해선 <slot> 요소에 속성으로 연결해야 한다.
<slot> 요소에 연결된 속성을 슬록 속성(slot props) 이라고 한다.
이제 부모 컴포넌트의 범위(scope)에서 v-slot에 연결한 슬롯 속성(slotProps)를 쓸 수 있다.

<!-- 자식 컴포넌트 -->
<span>
  <!-- v-bind를 이용해 동적 데이터 전달 -->
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>
<!-- 부모 컴포넌트 -->
<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>

단독 디폴트 슬롯을 위한 축약 문법

제공된 내용이 디폴트 슬롯 밖에 없으면 컴포넌트 태그를 슬록의 템플릿으로 바로 쓸 수 있다.
즉, v-slot을 컴포넌트에 쓸 수 있다는 것을 의미한다.

<!-- default slot -->
<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>
<!-- component + default slot -->
<current-user v-slot:default="slotProps">
  {{ slotProps.user.firstName }}
</current-user>
<!-- ↑ 같은 거 ↓ -->
<current-user v-slot="slotProps">
  {{ slotProps.user.firstName }}
</current-user>

이는 범위를 모호하게 만들기 때문에
디폴트 슬롯을 위한 축약 문법은 이름이 있는 슬롯들과 함께 쓸 수 없다.

<!-- 불가. 경고 뜸 -->
<current-user v-slot="slotProps">
  {{ slotProps.user.firstName }}
  <template v-slot:other="otherSlotProps">
    slotProps is NOT available here
  </template>
</current-user>

<!-- 이렇게 수정하여 사용해야 함 -->
<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
  <template v-slot:other="otherSlotProps">
    slotProps is NOT available here
  </template>
</current-user>

디스트럭처링(Destructuring Slot Props) - v-slot

프레임워크 내부에서 범위가 있는 슬롯은 하나의 인수를 가지는 함수로 슬롯에 들어가는 내용을 감싸는 방식으로 작동한다.
v-slot 값은 지원되는 환경(싱글 파일 컴포넌트 또는 최신 브라우저)에서 ES2015 디스트럭처링(구조분해)을 사용할 수 있다.

function (slotProps) {
  // ... slot content ...
}

// 1. 구조분해
<current-user v-slot="{ user }">
  {{ user.firstName }}
</current-user>

// 2. 구조분해 - 속성 이름
<current-user v-slot="{ user: person }">
  {{ person.firstName }}
</current-user>

// 3. 구조분해 - default 값
<current-user v-slot="{ user = { firstName: 'Guest' } }">
  {{ user.firstName }}
</current-user>

동적 Slot Names

동적 디렉티브 인수는 동적 슬롯 이름을 정의하는 방식으로 v-slot에서도 작동한다.

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

v-slot Shorthand

인수 앞에 쓰는 부분(v-slot:)을 특수 기호인 #으로 대체한다.

<base-layout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

다른 디렉티브와 마찬가지로 단축 표기는 오직 인수가 있을 때만 가능하다.

<!-- 불가. 경고 뜸 -->
<current-user #="{ user }">
  {{ user.firstName }}
</current-user>

단축 표기를 쓰려면 (default 라도) 반드시 슬롯의 이름이 있어야 한다.

<current-user #default="{ user }">
  {{ user.firstName }}
</current-user>


References

profile
까먹지마도토도토잠보🐘

0개의 댓글