출처: https://vuejs.org/guide/essentials/component-basics.html
컴포넌트를 사용하면 UI를 독립적이고 재사용 가능한 부분으로 분할하고 각 부분을 개별적으로 생각할 수 있습니다. 컴포넌트는 중첩될 수 있으며, 일반적으로 애플리케이션은 컴포넌트 트리로 구성됩니다.
App
├─ TodoList
│ ├─ TodoItem
│ │ ├─ TodoDeleteButton
│ │ └─ TodoEditButton
│ └─ TodoAddForm
└─ Conversation
├─ MessageList
│ └─ MessageItem
└─ MessageAddForm
컴포넌트 정의하기 (Defining a Component)
빌드 단계를 사용할 때, 일반적으로 각 Vue 컴포넌트를 전용 파일(예: .vue
파일)에 정의합니다. 이를 단일 파일 컴포넌트(Single-File Component, SFC)라고 합니다.
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
<style scoped>
button {
font-weight: bold;
}
</style>
컴포넌트 사용하기 (Using a Component)
자식 컴포넌트를 사용하려면 부모 컴포넌트에서 가져와야 합니다. ButtonCounter.vue
가 App.vue
와 동일한 디렉토리에 있다고 가정하면, 다음과 같이 가져올 수 있습니다.
<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>
<template>
<h1>Here are many child components!</h1>
<ButtonCounter />
<ButtonCounter />
<ButtonCounter />
</template>
<script setup>
을 사용하면 가져온 컴포넌트를 템플릿에서 직접 사용할 수 있습니다. 컴포넌트는 태그처럼 사용할 수 있습니다. 모든 컴포넌트는 여러 번 재사용할 수 있습니다.
Props 전달하기 (Passing Props)
컴포넌트는 재사용을 위해 만들어졌습니다. 부모에서 자식으로 데이터를 전달해야 할 가능성이 높습니다. 이를 위해 props를 사용합니다.
Props는 컴포넌트에 등록할 수 있는 사용자 정의 속성입니다. 값이 prop 속성에 전달되면 해당 컴포넌트 인스턴스의 속성이 됩니다. ButtonCounter
컴포넌트가 부모로부터 title
prop을 받도록 하려면 defineProps
매크로를 사용해야 합니다.
// ButtonCounter.vue
<script setup>
import { ref } from 'vue'
defineProps(['title'])
const count = ref(0)
</script>
<template>
<h4>{{ title }}</h4>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
이제 부모 컴포넌트에서 이 title
을 전달할 수 있습니다.
<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>
<template>
<h1>Here are many child components!</h1>
<ButtonCounter title="Give me a title" />
<ButtonCounter title="Give me another title" />
</template>
이벤트 수신하기 (Listening to Events)
자식 컴포넌트가 개발됨에 따라 부모 컴포넌트와 다시 통신해야 할 수도 있습니다. 예를 들어, <ButtonCounter>
컴포넌트가 클릭될 때마다 부모에게 알리고 싶을 수 있습니다.
이 솔루션은 Vue의 사용자 정의 이벤트 시스템입니다. 부모는 v-on
또는 @
를 사용하여 자식 컴포넌트의 모든 이벤트를 수신하도록 선택할 수 있습니다. 마치 네이티브 DOM 이벤트와 같습니다.
자식 컴포넌트는 내장된 $emit
메서드를 사용하여 템플릿 표현식(예: v-on
핸들러)에서 직접 사용자 정의 이벤트를 발생시킬 수 있습니다.
// ButtonCounter.vue
<script setup>
import { ref } from 'vue'
defineProps(['title'])
const emit = defineEmits(['enlarge-text'])
const count = ref(0)
</script>
<template>
<h4>{{ title }}</h4>
<button @click="count++">You clicked me {{ count }} times.</button>
<button @click="emit('enlarge-text')">Enlarge text</button>
</template>
부모는 이제 enlarge-text
이벤트를 수신하고 부모의 데이터를 업데이트할 수 있습니다.
<script setup>
import { ref } from 'vue'
import ButtonCounter from './ButtonCounter.vue'
const postFontSize = ref(1)
</script>
<template>
<div :style="{ fontSize: postFontSize + 'em' }">
<ButtonCounter
title="Give me a title"
@enlarge-text="postFontSize += 0.1"
/>
<ButtonCounter
title="Give me another title"
@enlarge-text="postFontSize += 0.1"
/>
</div>
</template>
슬롯을 이용한 콘텐츠 배포 (Content Distribution with Slots)
HTML 엘리먼트와 마찬가지로, 컴포넌트에 콘텐츠를 전달할 수 있는 것이 유용할 때가 많습니다. 예를 들면 다음과 같습니다.
<AlertBox>
Something bad happened.
</AlertBox>
이는 Vue의 <slot>
엘리먼트로 달성할 수 있습니다.
<!-- AlertBox.vue -->
<template>
<div class="alert-box">
<strong>This is an Error for Demo Purposes</strong>
<slot />
</div>
</template>
<style scoped>
.alert-box {
/* ... */
}
</style>
<slot>
엘리먼트는 부모가 전달한 슬롯 콘텐츠의 플레이스홀더 역할을 합니다. 슬롯 콘텐츠는 정적일 수도 있고, 반응형 데이터를 사용하여 동적일 수도 있습니다.
동적 컴포넌트 (Dynamic Components)
탭 인터페이스처럼 컴포넌트 간에 동적으로 전환하는 것이 유용할 때가 있습니다. 이는 Vue의 <component>
엘리먼트와 특별한 is
속성을 사용하여 가능합니다.
<script setup>
import { ref } from 'vue'
import Home from './Home.vue'
import Posts from './Posts.vue'
import Archive from './Archive.vue'
const currentTab = ref('Home')
const tabs = {
Home,
Posts,
Archive
}
</script>
<template>
<div class="demo">
<button
v-for="(_, tab) in tabs"
:key="tab"
:class="['tab-button', { active: currentTab === tab }]"
@click="currentTab = tab"
>
{{ tab }}
</button>
<component :is="tabs[currentTab]" class="tab"></component>
</div>
</template>
is
에 전달된 값은 컴포넌트의 이름 문자열이거나 실제 컴포넌트 객체일 수 있습니다.