티스토리 뷰

Vue.JS

[Vue.JS] 컴포넌트 (고급:slot)

버미노트 2019. 1. 26. 20:55

1. Slot Content

<navigation-link url="/profile">
  Your Profile
</navigation-link>

부모 컴포넌트에서 자식 컴포넌트를 위의 코드와 같이 사용하여 자식 컴포넌트의 하위 엘리먼트의 값들을 보여주고 싶다면,

<a
  v-bind:href="url"
  class="nav-link"
>
  <slot></slot>
</a>

위의 코드와 같이 자식 컴포넌트가 작성되어야 합니다. 위의 코드들이 랜더링 되면 <slot> 엘리먼트는 Your Profile이라는 문구로 대체됩니다. <slot>은 문자열 뿐만 아니라 모든 HTML과 대체 될 수 있습니다.

<navigation-link url="/profile">
  <!-- Add a Font Awesome icon -->
  <span class="fa fa-user"></span>
  Your Profile
</navigation-link>

위의 코드와 같은 기본 HTML 태그 뿐만 아니라,

<navigation-link url="/profile">
  <!-- Use a component to add an icon -->
  <font-awesome-icon name="user"></font-awesome-icon>
  Your Profile
</navigation-link>

위의 코드와 같은 사용자 정의 컴포넌트도 <slot>과 대체 될 수 있습니다. 자식 컴포넌트에서 <slot>을 사용하지 않았다면, 부모 컴포넌트에서 자식 컴포넌트의 하위 엘리먼트로 넘겨 준 값들은 모두 무시 됩니다.

2. Named Slots

부모 컴포넌트에서 자식 컴포넌트로 여러개의 하위 엘리먼트를 넘겨줄 때 이름이 있는 <slot>을 사용한다면 특정 위치를 지정할 수 있습니다.

<div class="container">
  <header>
    <!-- We want header content here -->
  </header>
  <main>
    <!-- We want main content here -->
  </main>
  <footer>
    <!-- We want footer content here -->
  </footer>
</div>

예를 들면, 위의 코드와 같이 <base-layout>이라는 컴포넌트가 있습니다. 하당 컴포넌트에는 <header><main>, <footer> 3개의 정보가 채워져야 합니다.

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div> 

이 경우에 위의 코드와 같이 이름이 있는 <slot>을 사용할 수 있습니다.

<base-layout>
  <template slot="header">
    <h1>Here might be a page title</h1>
  </template>

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

  <template slot="footer">
    <p>Here's some contact info</p>
  </template>
</base-layout>

부모 컴포넌트에서는 자식 컴포넌트인 <base-loyout> 컴포넌트를 위의 코드와 같이 사용할 수 있습니다.

<base-layout>
  <h1 slot="header">Here might be a page title</h1>

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

  <p slot="footer">Here's some contact info</p>
</base-layout> 

위의 코드와 같이 <template>를 사용하지 않고 직접 엘리먼트에 slot 속성을 사용할 수도 있습니다. 이름이 없는 기본 <slot>은 항상 하나만 존재해야 합니다.

<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

<base-layout> 컴포넌트 예제는 결국 위의 코드와 같이 랜더링됩니다.

3. Default Slot Content

slot은 기본값을 제공합니다.

<button type="submit">
  <slot>Submit</slot>
</button>

위의 코드와 같이 <submit-button> 컴포넌트가 있을 때, <submit-button> 컴포넌트의 하위 엘리먼트가 제공되지 않는다면, 기본값으로 Submit이 화면에 보이게 됩니다. <submit-button> 컴포넌트의 하위 엘리먼트가 존재한다면, Submit이 아닌 <submit-button> 컴포넌트의 하위 엘리먼트가 화면에 나타나게 됩니다.

4. Compilation Scope

slot에 data를 사용하고 싶을 때,

<navigation-link url="/profile">
  Logged in as {{ user.name }}
</navigation-link>

위의 코드와 같이 사용할 수 있습니다. 이 때 사용한 user.name<navigation-link> 컴포넌트의 스코프에 있는 값이 아닌, <navigation-link> 컴포넌트를 사용한 부모 컴포넌트의 스코프에 있는 값입니다.

5. Scoped Slots

이 기능은 2.1.0 이상의 Vue 버전에서 제공하는 기능입니다.

slot을 사용할 때 하위 컴포넌트의 데이터를 부모 컴포넌트에서 사용해야 할 때가 있습니다. 예를 들어,

<ul>
  <li
    v-for="todo in todos"
    v-bind:key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

위의 코드와 같이 <todo-list>라는 컴포넌트가 있을 때, 위의 컴포넌트는 <toto-list> 컴포넌트 스코프 안에 있는 todos의 값을 나타냅니다. 부모 컴포넌트에서 자식 컴포넌트의 data를 사용하기 위해서는,

<ul>
  <li
    v-for="todo in todos"
    v-bind:key="todo.id"
  >
    <!-- We have a slot for each todo, passing it the -->
    <!-- `todo` object as a slot prop.                -->
    <slot v-bind:todo="todo">
      <!-- Fallback content -->
      {{ todo.text }}
    </slot>
  </li>
</ul>

자식 컴포넌트인 <todo-list> 컴포넌트에서는 위의 코드와 같이 작성되어야 합니다.

<todo-list v-bind:todos="todos">
  <!-- Define `slotProps` as the name of our slot scope -->
  <template slot-scope="slotProps">
    <!-- Define a custom template for todo items, using -->
    <!-- `slotProps` to customize each todo.            -->
    <span v-if="slotProps.todo.isComplete">✓</span>
    {{ slotProps.todo.text }}
  </template>
</todo-list>

부모 컴포넌트에서는 위의 코드와 같이 작성하여 자식 컴포넌트의 data를 사용할 수 있습니다. 이 때 slot-scope<template>에서 사용 되었지만 2.5.0 이상의 버전에서는 일반 엘리먼트에서도 사용 가능해 졌습니다.

1) Destructuring slot-scope

slot-scope에 구조분해 문법 사용이 가능합니다.

<todo-list v-bind:todos="todos">
  <template slot-scope="{ todo }">
    <span v-if="todo.isComplete">✓</span>
    {{ todo.text }}
  </template>
</todo-list>

위의 코드와 같이 구조분해 문법을 사용하면 좀 더 간결한 코드가 될 수 있습니다.

참고

댓글
공지사항
최근에 올라온 글