<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>
<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>
범위가 지정된 슬롯은 이미 렌더링 된 엘리먼트 대신 재사용 가능한 템플릿(데이터를 전달할 수 있음)으로 작동하는 특별한 유형의 슬롯이다.
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>
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>
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>
동적 디렉티브 인수는 동적 슬롯 이름을 정의하는 방식으로 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>