Vue.js Starter - Component
Vue.js νλ‘μ νΈ ν¬μ μΌμ£ΌμΌ μ - Part 4
13. Nested Component - Props π©βπ»
1. Parent/Child Component and Props
- Parent Component
1 )
Component TagsλPascalCaseλ₯Ό μ¬μ©νλ€.<template> <MyComponent /> </template> <script> import MyComponent from "@/components/MyComponent.vue"; export default { components: { MyComponent } } </script>2 ) μμ μ»΄ν¬λνΈμ μ λ¬ν
Propsλ HTML attributes μ convention μ λ§μΆκΈ° μν΄kebab-caseλ₯Ό μ¬μ©νλ©°, ν€μ μ€λ³΅μ λ§κΈ° μν΄ μ΄λ¦μ μΆμ½ μμ΄ κΈΈκ² μ μΈνλ€.<template> <MyComponent greeting-message="hello" /> </template>3 )
Dynamic Propsλ₯Ό μ λ¬νκΈ° μν΄v-bindλλ:λ₯Ό μ¬μ©νλ€.<template> <MyComponent :greeting-message="post.title" /> </template>
- Child Component
1 ) λΆλͺ¨ μ»΄ν¬λνΈκ° μ λ¬ν λ°μ΄ν°λ
propsproperty μObjectλ₯Ό λ§€νν΄ λ°λλ€. μ΄λ μ΄λ¦μ JavaScript μ μλ³μ κ·μΉμ λ§κ²camelCaseλ₯Ό μ¬μ©νλ€.<script> export default { props: { greetingMessage: String } } </script>2 )
propsλTypeScriptλ₯Ό μ¬μ©νμ§ μκ³ λ λΆλͺ¨μκ² μ λ¬ λ°μData Typeμ μ§μ ν μ μκ² λμμ€λ€. μμΈν Type μ²΄ν¬ λ° Validation μ 5. Prop Validation λ₯Ό μ°Έκ³ νλ€.
2. Static Props
Child Component PageTitleλ₯Ό λ§λ λ€.
- /src/components/PageTitle.vue
<template>
<h2>{{ myTitle }}</h2>
</template>
<script>
export default {
name: "PageTitle",
props: {
myTitle: { type: String, default: "νμ΄μ§ μ λͺ©μ
λλ€." },
},
};
</script>
PageTitle μ»΄ν¬λνΈλ₯Ό λΆλͺ¨ μ»΄ν¬λνΈμ μ£Όμ
μν€κ³ , Static Propsλ‘ λ°μ΄ν°λ₯Ό μ λ¬ν΄λ³΄μ.
- /src/views/AboutView.vue
<template>
<PageTitle my-title="About νμ΄μ§μ
λλ€." />
</template>
<script>
import PageTitle from "@/components/PageTitle.vue";
export default {
name: "AboutView",
components: {
PageTitle,
},
};
</script>

3. Dynamic Props
Value μ체λ₯Ό μ λ¬νλ κ²μ΄ μλ λ³μ/μμλ₯Ό μ λ¬νλ €λ©΄ v-bind λλ μΆμ½νμΈ :λ₯Ό μ¬μ©νλ€.
- /src/views/AboutView.vue
<template>
<PageTitle :my-title="someTitle" />
</template>
<script>
import PageTitle from "@/components/PageTitle.vue";
export default {
name: "AboutView",
components: {
PageTitle,
},
data() {
return {
someTitle: "About νμ΄μ§μ
λλ€.",
};
},
};
</script>
μμ μ»΄ν¬λνΈλ λΆλͺ¨ μ»΄ν¬λνΈμμ μ λ¬νλ λ°μ΄ν°κ°
Static PropsμΈμ§,Dynamic PropsμΈμ§ μ μ μλ€. λ¨μ§ μ λ¬ λ°μ νμ λ§ μ μνκ³ κ·Έ κ°(Value Types or Reference Types)μ λ°μ λΏμ΄λ€.
λ³λμ λ³μ μμ΄ μ λ¬νλ €λ©΄ λ€μκ³Ό κ°μ΄ IIFEλ₯Ό μ¬μ©ν μ μλ€.
<template>
<PageTitle :my-title="(() => 'About νμ΄μ§μ
λλ€.')()" />
</template>
Static PropsλStringλ§ μ λ¬ν μ μλ€. λ°λΌμ Number, Boolean, Array, Object, Property of Objectμ κ°μ λ°μ΄ν°λ λͺ¨λDynamic Propsλ₯Ό μ¬μ©ν΄ μ λ¬ν΄μΌνλ€.
4. One-way Data Flow
Vue μ체λ Two-way data bindingλ₯Ό μ§μνμ§λ§, λΆλͺ¨ μμ μ»΄ν¬λνΈ μ¬μ΄λ One-way down bindingμΌλ‘ μλνλ€.
μ΄λ μμ μ»΄ν¬λνΈμ μν΄ λΆλͺ¨ μ»΄ν¬λνΈμ stateκ° λ³κ²½λλ κ²μ νμ©νκ² λλ©΄ μ±μ λ°μ΄ν° νλ¦μ μ΄ν΄νκΈ°
μ΄λ ΅κ² λ§λ€κΈ° λλ¬Έμ΄λ€.
Vueμ μν΄Two-way data bindingμ΄ μ λλ€λ κ² λΏμ΄μ§ μμ μ»΄ν¬λνΈμμ λΆλͺ¨ μ»΄ν¬λνΈλ‘ λ°μ΄ν° μ λ¬μ΄ λΆκ°λ₯ν κ²μ μλλ€.Vueμ μν΄bindingμ΄ λμ§ μκ³ , μμ μ»΄ν¬λνΈκ° λΆλͺ¨ μ»΄ν¬λνΈλ₯Ό λ³κ²½ν μ μκΈ° λλ¬Έμ λΆλͺ¨ μ»΄ν¬λνΈ μͺ½μμ λ₯λμ μΌλ‘ λ°μ΄ν°λ₯Ό λ°μνλ λ°©λ²μ μ¬μ©ν μ μλ€.
- μμ μ»΄ν¬λνΈκ° λΆλͺ¨ μ»΄ν¬λνΈμ ν¨μμ λ°μ΄ν°λ₯Ό arguments λ‘ λμ Έ μ¬λ¦¬κ³ , λΆλͺ¨ μ»΄ν¬λνΈμ ν¨μκ° μ΄ λ°μ΄ν°λ₯Ό μμ μ λ³μμ μ μ₯νλ€. See: Emitting and Listening to Events
- λ€λ₯Έ λ°©λ²μΌλ‘λ λΆλͺ¨ μ»΄ν¬λνΈκ° μμ μ»΄ν¬λνΈμ λ°μ΄ν°λ₯Ό
Template Refsλ₯Ό μ΄μ©ν΄ λ³νλ₯Ό κ°μνλλ‘ λΆλͺ¨ μ»΄ν¬λνΈ λ΄μComputed Propertiesλ₯Ό μ μΈνκ³ , λ³νλ₯Ό κ°μν κ°μTemplate Refsλ₯Ό μ΄μ©ν΄ μμ μ»΄ν¬λνΈμ λ³μλ₯Ό μ§μ νλ€. See:
1 ) Make Props like Local Variable
μ¦, λΆλͺ¨ μ»΄ν¬λνΈμμ μ λ¬λ μμ μ»΄ν¬λνΈλ down binding μνμ΄λ―λ‘ λΆλͺ¨μ μνκ° λ³νλ©΄ κ°μ΄ λ³νκ² λλ€.
λ°λΌμ, μμ μ»΄ν¬λνΈμμ μ΄λ₯Ό local variableμ²λΌ μ¬μ©νκΈ°λ₯Ό μνλ€λ©΄ λ€μκ³Ό κ°μ΄ λ³λμ λ³μμ κ°μ μ μ₯ν΄
μ¬μ©νλ€.
<script>
export default {
props: ['initialCounter'],
data() {
return {
// counter only uses this.initialCounter as the initial value;
// it is disconnected from future prop updates.
counter: this.initialCounter
}
}
}
</script>
2 ) Props with Computed Properties
λ³μμ κ°μ μ μ₯ν λ Observerλ₯Ό μ΄μ©ν΄ κ°μνκ³ , μ¬μ©μ μ μ λ‘μ§μ μΆκ°ν΄ μ μ₯ν μ μλ€.
νμ§λ§, Vueμ Watchμ κ²½μ° λΆλͺ¨ μ»΄ν¬λνΈμμ λμ΄μ€λ λ°μ΄ν°λ₯Ό μμ μ»΄ν¬λνΈ λ³μμ willSetμ΄ μκ³
didSetλ§ μλ€λ³΄λ(Vue Watch Property) Vue 곡μ λ¬Έμλ₯Ό 보면 μ΄λ° κ²½μ° Compouted Propertiesλ₯Ό
μ¬μ©νλλ‘ νκ³ μλ€.
<script>
export default {
props: ['size'],
computed: {
// computed property that auto-updates when the prop changes
normalizedSize() {
return this.size.trim().toLowerCase()
}
}
}
</script>
5. Prop Validation
λ¨μν κ°μ μ λ¬νλ κ² μΈμ λ€μκ³Ό κ°μ΄ μ ν¨μ± κ²μ¬λ₯Ό μΆκ°ν μ μλ€.
export default {
props: {
// Basic type check
// (`null` and `undefined` values will allow any type)
propA: Number,
// Multiple possible types
propB: [String, Number],
// Required string
propC: {
type: String,
required: true
},
// Number with a default value
propD: {
type: Number,
default: 100
},
// Object with a default value
propE: {
type: Object,
// Object or array defaults must be returned from
// a factory function. The function receives the raw
// props received by the component as the argument.
default(rawProps) {
return { message: 'hello' }
}
},
// Custom validator function
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// Function with a default value
propG: {
type: Function,
// Unlike object or array default, this is not a factory function - this is a function to serve as a default value
default() {
return 'Default function'
}
}
}
}
14. Nested Component - Template Refs and Event Call π©βπ»
1. Template Refs ($ref) (Parent to Child)
HTMLμμ id attribute κ° uniqueν μμ±μ κ°μ§ κ²μ²λΌ, Vueμμλ ref attribute κ° μ΄ μν μ νλ€.
λ°λΌμ, Vueμμ μ΄λ€ μ»΄ν¬λνΈ λλ Real DOMμ μ κ·Όνκ³ μΆλ€λ©΄ refλ₯Ό μ¬μ©ν΄ μ κ·Όν μ μλ€.
Vueμμ Real DOMμ μ κ·Όνκ³ μ΄λ₯Ό λ€λ£¨λ κ²μ΄ μ’μ λ°©λ²μ μλμ§λ§, refλ₯Ό μ΄μ©νλ©΄ λΆλͺ¨ μ»΄ν¬λνΈκ° μμ μ»΄ν¬λνΈμ
DOM μ μ κ·Όν΄ click μ΄λ²€νΈλ₯Ό νΈμΆνκ±°λ, μμ μ»΄ν¬λνΈ λ΄μ μ μλ ν¨μλ₯Ό νΈμΆνκ±°λ, μμ μ»΄ν¬λνΈ λ΄μ μ μλ λ³μμ μ κ·Όνλ
κ²κ³Ό κ°μ λͺ¨λ νμκ° κ°λ₯νλ€(window.openerλ‘ μ κ·Όν λμ λΆλͺ¨ μμ μ°½ κ΄κ³μ μ μ¬νλ€).
2. Emitting and Listening to Events ($emit) (Child to Parent)
λ°λ©΄, μμ μ»΄ν¬λνΈμμ λΆλͺ¨ μ»΄ν¬λνΈμ 무μΈκ° μ§μ μ μΌλ‘ λ°μ΄ν°λ₯Ό μ λ¬ν μ μλ λ°©λ²μ λΆλͺ¨ μ»΄ν¬λνΈμ μ μλμ΄ μμΌλ©°, μμ μ»΄ν¬λνΈμ
binding λ ν¨μλ₯Ό νΈμΆνκΈ° μν΄ emittingνλ κ²λ§ νμ©λλ€. νμλ―Ό μ νν μκΈ°νλ©΄, λΆλͺ¨ μ»΄ν¬λνΈκ° μμ μ»΄ν¬λνΈμ μ 곡ν ν¨μκ°
νΈμΆ μμ²μ΄ μλμ§ listeningνκ³ μκ³ , μμ μ»΄ν¬λνΈμμ λΆλͺ¨ μ»΄ν¬λνΈμμ μ 곡λ ν¨μλ₯Ό νΈμΆνλ μμ²κ³Ό ν¨κΉ¨ argumentsλ₯Ό
μ λ¬ν΄ μ¬λ¦¬λ emittingμ΄ λ°μλ¨μΌλ‘ μΈν΄ μμ μ»΄ν¬λνΈμμ λΆλͺ¨ μ»΄ν¬λνΈμ ν¨μλ₯Ό νΈμΆνλ κ²μ²λΌ 보μ΄λ κ²μΌ λΏμ΄λ€.
Propsμ λ§μ°¬κ°μ§λ‘, Parent Componentμμ Component Tagsμ attribute ννλ‘ λ±λ‘λλv-bindλ λͺ¨λkebab-caseλ₯Ό μ¬μ©νκ³ ,Child Componenetμμ μ΄κ²μ binding ν λ³μλ λͺ¨λcamelCaseλ₯Ό μ¬μ©νλ€.
3. Computed Properties (Child to Parent)
Computed Propertiesμ νκ²μ΄ μκΈ° μμ μ μ»΄ν¬λνΈμ λ°μ΄ν°μΌ κ²½μ°Two-way bindingλ λκ³ μλ¬΄λ° μ μ½μ΄ μλ κ²κ³Ό λ¬λ¦¬ νκ²μTemplate Refsλ₯Ό μ΄μ©ν΄ μμ μ»΄ν¬λνΈμ κ°μ κ°μν κ²½μ°, μ΄ λ³μλ λΆλͺ¨ μ»΄ν¬λνΈμscriptλ΄μμλ§ μ¬μ©μ΄ κ°λ₯νλ€.
(templateμ μ¬μ©λ λ³μλTwo-way data bindingμΌλ‘ μλνλλ°, μ΄λtemplateμ λ³κ²½ λ΄μμ΄scriptμ μ μλComputed Propertyλ‘ μ λ¬ λκ³ , μ΄λ λ€μ νκ²μΈ μμ μ»΄ν¬λνΈμ λ°μ΄ν°λ₯Ό ν₯νκ² λλ€. λ¬Έμ λ μμ μ»΄ν¬λνΈμ λ³κ²½ μ¬νμ λΆλͺ¨ μ»΄ν¬λνΈμμ λ₯λμ μΌλ‘ μ μ΄λλ κ²μΌ λΏ, μμ μ»΄ν¬λνΈμμ λΆλͺ¨ μ»΄ν¬λνΈλ‘ μ λ¬μ νμ§ λͺ» νλ―λ‘Two-way data bindingμ νλ¦μ΄ λμ΄μ§λ€.)
4. Template Refs and Event Call Examples
- /src/views/EventCallView.vue
<template>
<h1>Parent Component</h1>
<button type="button" @click="parentFunc">
λΆλͺ¨ μ»΄ν¬λνΈμμ λΆλͺ¨ μ»΄ν¬λνΈ μ΄λ²€νΈ νΈμΆ
</button>
<button type="button" @click="callChildEvent">
λΆλͺ¨ μ»΄ν¬λνΈμμ μμ μ»΄ν¬λνΈ μ΄λ²€νΈ νΈμΆ(Real DOM μ μ κ·Ό)
</button>
<button type="button" @click="callChildFunc">
λΆλͺ¨ μ»΄ν¬λνΈμμ μμ μ»΄ν¬λνΈ μ΄λ²€νΈ νΈμΆ(ν¨μμ μ§μ μ κ·Ό)
</button>
<button type="button" @click="setChildVariable">
λΆλͺ¨ μ»΄ν¬λνΈμμ μμ μ»΄ν¬λνΈ λ°μ΄ν° 'alpha' λ³κ²½
</button>
<!-- <p>-->
<!-- μμ μ»΄ν¬λνΈμ λ°μ΄ν°λ₯Ό 'beta' λ₯Ό computed νλ λΆλͺ¨ μ»΄ν¬λνΈμ λ°μ΄ν°:-->
<!-- {{ syncedWithChildComponentVariable }}-->
<!-- </p>-->
<button type="button" @click="popSyncedVariable">
μμ μ»΄ν¬λνΈμ λ°μ΄ν° 'beta' λ₯Ό computed νλ λΆλͺ¨ μ»΄ν¬λνΈμ λ°μ΄ν° νμ
</button>
<hr />
<ChildComponent @parent-Func="parentFunc" ref="childComponent" />
</template>
<script>
import ChildComponent from "@/components/ChildComponent.vue";
export default {
name: "EventCallView",
components: {
ChildComponent,
},
methods: {
parentFunc() {
alert("λΆλͺ¨ μ»΄ν¬λνΈ μ΄λ²€νΈκ° νΈμΆλμμ΅λλ€.");
},
callChildEvent() {
this.$refs.childComponent.$refs.myChildButton.click();
},
callChildFunc() {
this.$refs.childComponent.childFunc();
},
setChildVariable() {
this.$refs.childComponent.alpha = Math.trunc(Math.random() * 10);
},
popSyncedVariable() {
alert(this.syncedWithChildComponentVariable);
},
},
computed: {
syncedWithChildComponentVariable() {
return this.$refs.childComponent.beta;
},
},
};
</script>
- /src/components/ChildComponent.vue
<template>
<h1>Child Component</h1>
<button type="button" @click="childFunc" ref="myChildButton">
μμ μ»΄ν¬λνΈμμ μμ μ»΄ν¬λνΈ μ΄λ²€νΈ νΈμΆ
</button>
<button type="button" @click="callParentFunc">
μμ μ»΄ν¬λνΈμμ λΆλͺ¨ μ»΄ν¬λνΈ μ΄λ²€νΈ νΈμΆ
</button>
<button type="button" @click="setSelfVariable">
μμ μ»΄ν¬λνΈμμ μμ μ»΄ν¬λνΈ λ°μ΄ν° 'beta' λ³κ²½
</button>
<p>alpha: {{ alpha }}</p>
<p>beta: {{ beta }}</p>
</template>
<script>
export default {
name: "ChildComponent",
data() {
return {
alpha: 0,
beta: 0,
gamma: "abc",
};
},
methods: {
childFunc() {
alert("μμ μ»΄ν¬λνΈμ μ΄λ²€νΈκ° νΈμΆλμμ΅λλ€.");
},
callParentFunc() {
this.$emit("parentFunc");
},
setSelfVariable() {
this.beta = Math.trunc(Math.random() * 10);
},
},
};
</script>

15. Nested Component - Slots π©βπ»
1. Slot Content and Outlet
μΌκ΄λ λμμΈμ UI/UXμ λ§€μ° μ€μνλ€. νμ
μ°½μ μλ‘ λ€λ©΄, λμΌν νμ
μ΄λΌλ κ°λ°μκ° λ§€λ² μ§μ ꡬνν κ²½μ°
μ€μλ μλ‘ λ€λ₯Έ κ°λ°μμ μν΄ κ°λ°μμ μ£Όκ΄μ΄ μμ΄κ² λλ λ€λ₯Έ λΆλΆμ΄ λνλκ² λλ€.
곡ν΅ν λ° μ¬μ¬μ©μ μν΄ Vue λ Componenetsλ₯Ό μ΄μ©νλ€. νμ§λ§ λ¨μν λͺ¨λ¬μ°½, νμ΄νκ³Ό κ°μ μ»΄ν¬λνΈλ
λΆλͺ¨ μμκ° propsλ₯Ό μ΄μ©ν΄ λ°μ΄ν°λ₯Ό μ λ¬νκ³ μ λΆ κ΅¬νν΄μΌνλ λΆνΈν¨μ΄ μλ€. μ΄λ° κ³΅ν΅ μ»΄ν¬λνΈ λ΄μ Slotsμ μ΄μ©νλ©΄
HTMLμ μμ±ν΄ κ·Έλλ‘ μ£Όμ
νλ κ²μ΄ κ°λ₯ν΄ κ°λ²Όμ΄ λ μ΄μμμ μ½κ² μ¬μ¬μ© ν μ μλ€.
- /src/components/PageTitle.vue
2. Static Props μμ /src/components/PageTitle.vue λ₯Ό μ΄μ©ν΄ νμ΄μ§μ νμ΄νμ 곡ν΅ννλ€.
<template>
<h2>{{ myTitle }}</h2>
</template>
<script>
export default {
name: "PageTitle",
props: {
myTitle: { type: String, default: "νμ΄μ§ μ λͺ©μ
λλ€." },
},
};
</script>
- /src/components/common/SlotPageTitle.vue
μ΄κ²μ SlotμΌλ‘ λ°κΎΈλ©΄ λ€μκ³Ό κ°λ€.
<template>
<h2><slot /></h2>
</template>
<script>
export default {
name: "SlotPageTitle",
};
</script>
λΆλͺ¨ μ»΄ν¬λνΈ μμ λ¨μ μ»΄ν¬λνΈλ₯Ό μ¬μ¬μ© ν λμ Slotμ μ¬μ©ν μ»΄ν¬λνΈλ₯Ό μ¬μ¬μ© ν λ μ½λλ λ€μκ³Ό κ°μ΄
λ³κ²½λλ€.
- /src/views/AboutView.vue (with Ordinary Component)
<template>
<PageTitle my-title="About νμ΄μ§μ
λλ€." />
</template>
<script>
import PageTitle from "@/components/PageTitle.vue";
export default {
name: "AboutView",
components: {
PageTitle,
},
};
</script>
- /src/views/AboutView.vue (with Slot Component)
<template>
<SlotPageTitle> About νμ΄μ§μ
λλ€. </SlotPageTitle>
</template>
<script>
import SlotPageTitle from "@/components/common/SlotPageTitle.vue";
export default {
name: "AboutView",
components: {
SlotPageTitle,
},
};
</script>

2. Named Slots
λ¨μΌ Slot μ΄ μλ κ²½μ° κ΅¬λΆνκΈ° μν΄ 'name' μ΄ νμνλ€. κ° name μ element λ₯Ό μ΄μ©ν΄ μ½μ
νκ³ ,
v-slot attribute λ₯Ό μ΄μ©ν΄ μ°κ²°ν μ μλ€.
μ¬λ¬ Slots μ€ νλμ Slot μ νν΄ name μ μλ΅ν μ μλλ°, μ΄ κ²½μ° v-slot:defaultμ μ΄μ©ν΄ μ°κ²°νλ€.
header, main, footer λ‘ κ΅¬μ±λ νμ
λͺ¨λ¬ λ μ΄μμμ SlotμΌλ‘ λ§λ€μ΄ μ μ©ν΄λ³΄μ.
- /src/components/common/SlotModalLayout.vue
<template>
<div class="modal-container">
<header>
<h1>
<slot name="header"></slot>
</h1>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<script>
export default {
name: "SlotModalLayout",
};
</script>
<style scoped>
.modal-container {
width: 500px;
--modal-border: 30px;
}
.modal-container > header {
height: 50px;
background: aquamarine;
border-top-left-radius: var(--modal-border);
border-top-right-radius: var(--modal-border);
}
.modal-container > main {
}
.modal-container > footer {
height: 40px;
background: aquamarine;
border-bottom-left-radius: var(--modal-border);
border-bottom-right-radius: var(--modal-border);
}
</style>
- /src/views/SlotModalLayoutView.vue
<template>
<button type="button" @click="openPopup">
{{ popupState ? "Close Popup" : "Open Popup!!" }}
</button>
<hr />
<SlotModalLayout v-show="popupState === true">
<template v-slot:header> νμ
νμ΄ν </template>
<template v-slot:default>
<p>μλ¦Ό 1 : μλ
νμΈμ</p>
<p>μλ¦Ό 2 : λ°κ°μ΅λλ€</p>
</template>
<template v-slot:footer>
<button type="button" @click="openPopup">λ«κΈ°</button>
</template>
</SlotModalLayout>
</template>
<script>
import SlotModalLayout from "@/components/common/SlotModalLayout.vue";
export default {
name: "SlotModalLayoutView",
data() {
return {
popupState: false,
};
},
components: {
SlotModalLayout,
},
methods: {
openPopup() {
this.popupState = !this.popupState;
},
},
};
</script>
κ·Έλ¦¬κ³ v-slot:μ #μ μ΄μ©ν΄ λ¨μΆνμΌλ‘ μμ±ν μ μλ€.
<template>
<button type="button" @click="openPopup">
{{ popupState ? "Close Popup" : "Open Popup!!" }}
</button>
<hr />
<SlotModalLayout v-show="popupState === true">
<template #header> νμ
νμ΄ν </template>
<template #default>
<p>μλ¦Ό 1 : μλ
νμΈμ</p>
<p>μλ¦Ό 2 : λ°κ°μ΅λλ€</p>
</template>
<template #footer>
<button type="button" @click="openPopup">λ«κΈ°</button>
</template>
</SlotModalLayout>
</template>

3. Slot Examples
1. Slot Content and Outlet, 2. Named Slots μ μΆκ°λ‘
λ²νΌ μ€νμΌμ 곡ν΅ν νλ Vue.js documents μμ λ₯Ό νλ λ μκ°νλ€.
- /src/components/common/FancyButton.vue
<template>
<button class="fancy-btn">
<slot />
</button>
</template>
<style scoped>
.fancy-btn {
color: #fff;
background: linear-gradient(315deg, #42d392 25%, #647eff);
border: none;
padding: 5px 10px;
margin: 5px;
border-radius: 8px;
cursor: pointer;
}
</style>
- /src/components/common/AwesomeIcon.vue
<!-- using an emoji just for demo purposes -->
<template>β€οΈ</template>
- /src/views/FancyButtonView.vue
<template>
<FancyButton> Click me </FancyButton>
<FancyButton>
<span style="color: cyan">Click me! </span>
<AwesomeIcon />
</FancyButton>
</template>
<script>
import FancyButton from "@/components/common/FancyButton.vue";
import AwesomeIcon from "@/components/common/AwesomeIcon.vue";
export default {
name: "FancyButtonView",
components: {
FancyButton,
AwesomeIcon,
},
};
</script>

16. Nested Component - Provide/Inject π©βπ»
Propsλ₯Ό μ¬μ©νλ©΄ λΆλͺ¨ μ»΄ν¬λνΈμμ μμ μ»΄ν¬λνΈλ‘ λ°μ΄ν°λ₯Ό μ λ¬ν μ μλ€. λ¬Έμ λ κ³μΈ΅μ΄ 1 κ³μΈ΅μ΄ μλ κ²½μ°
λΆλͺ¨μμ μμμκ², λ λ€μ λΆλͺ¨μμ μμμκ²β¦ μ΄λ°μμΌλ‘ μ¬λ¬ μ°¨λ‘ λ°λ³΅ν΄ λ΄λ €κ°μΌ νλ€λ λ¬Έμ κ° μλ€.

λ°λΌμ μ΄λ° κ²½μ°
Provideλ‘ μ 곡νκ³ ,Injectλ‘ μ£Όμ νλ©΄ μ¬λ¬ κ³μΈ΅μΌλ‘ ꡬμ±λ large tree μ»΄ν¬λνΈμμλDeepChildκΉμ§ ν λ²μ μ£Όμ μ΄ κ°λ₯μΌ νλ€Prop Drilling.λ¨,
Provideλ₯Ό ν΅ν΄ μ 곡λ λ°μ΄ν°λ μμ μ»΄ν¬λνΈμμ μ£Όμ ν λ μ΄λ€ μμ μ»΄ν¬λνΈμμ μ¨ λ°μ΄ν°μΈμ§ μ μ μλ€λ λ¨μ μ΄ μ‘΄μ¬νλ€.
1. Provide
- /src/views/RootView.vue
<template>
<FirstChild />
</template>
<script>
import FirstChild from "@/components/FirstChild.vue";
export default {
name: "RootView",
components: {
FirstChild,
},
provide() {
return {
rootValue: "Hello~ I'm root.",
};
},
};
</script>
- /src/components/FirstChild.vue
<template>
<SecondChild />
</template>
<script>
import SecondChild from "@/components/SecondChild.vue";
export default {
name: "FirstChild",
components: {
SecondChild,
},
};
</script>
- /src/components/SecondChild.vue
<template>
<ThirdChild />
</template>
<script>
import ThirdChild from "@/components/ThirdChild.vue";
export default {
name: "SecondChild",
components: {
ThirdChild,
},
};
</script>
2. App-level Provide
Provideλ₯Ό App-levelμμ μ 곡νλ©΄ λͺ¨λ μ»΄ν¬λνΈμμ μ¬μ©ν μ μλ€. μΌλ°μ μΌλ‘ νλ¬κ·ΈμΈμ μ»΄ν¬λνΈλ₯Ό μ΄μ©ν΄ κ°μ μ 곡ν μ
μκΈ° λλ¬Έμ νλ¬κ·ΈμΈμ μ± μ μμ μμ±ν λ μ μ©νλ€.
- /src/main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import mixins from "@/mixins";
createApp(App)
.use(store)
.use(router)
.mixin(mixins)
.provide("appLevelValue", "Hello~ This is App")
.mount("#app");
3. Inject
- /src/components/ThirdChild.vue
Provide λ λ°μ΄ν°λ₯Ό μ»΄ν¬λνΈμ μ£Όμ
νλ λ°©λ²μ Arrayλ₯Ό μ΄μ©νλ κ²κ³Ό Objectλ₯Ό μ΄μ©νλ κ² λ κ°μ§κ° μλ€.
Arrayλ₯Ό μ΄μ©ν΄ λ¨μ μ£Όμ νκΈ°
<template>
<p>This message is come from root : {{ rootValue }}</p>
<p>This message is come from app : {{ appLevelValue }}</p>
</template>
<script>
export default {
name: "ThirdChild",
inject: ["rootValue", "appLevelValue"],
};
</script>

Objectλ₯Ό μ΄μ©ν΄key-valueνμ μΌλ‘ μ£Όμ νκΈ°
<template>
<p>This message is come from root : {{ rootMessage }}</p>
<p>This message is come from app : {{ appMessage }}</p>
</template>
<script>
export default {
name: "ThirdChild",
inject: {
rootMessage: "rootValue",
appMessage: "appLevelValue",
},
};
</script>
Objectλ₯Ό μ΄μ©ν΄key-valueνμ μΌλ‘ μ£Όμ νλ©΄, μμ μ»΄ν¬λνΈμμ μ 곡νProvideμ λ³μλͺ λμ μμ μ»΄ν¬λνΈμμ μμ±ν λ³μλͺ μ μ¬μ©νλ κ²μ΄ κ°λ₯νλ€.
Reference
- κ³ μΉμ. Vue.js νλ‘μ νΈ ν¬μ μΌμ£ΌμΌ μ . λΉμ μ΄νΌλΈλ¦ Chapter 8, 2021.