[DAY-38] Vue (5)
π ν·κ°λ Έλ & λͺ°λλ λΆλΆλ€λ§ μ 리νλ λλ§μ TIL
π― λͺ¨λ κ°μ λ΄μ©μ μ μ§ μμμ!
μ€λμ μκ°μ?
Vueμ μ»΄ν¬λνΈ κ΅¬μ‘°, λ°μ΄ν° λ°©ν₯ λ±μ νμ
ν μ μμμ΅λλ€.
νμ§λ§ μμ§ λ©μμ΄μ..
κ³Όμ μ΄λ‘νμ§... μ΄λ‘νμ§....
[1] μ»΄ν¬λνΈ λ±λ‘
μ»΄ν¬λνΈλ μ μ, μ§μ λ±λ‘μ΄ κ°λ₯ν©λλ€.
λ±λ‘μ μν΄μλ μ»΄ν¬λνΈμ μ΄λ¦μ λ°λμ μ§μ ν΄μΌ ν©λλ€.
ν΄λΉ μ»΄ν¬λνΈλ νμ€μΉΌ μΌμ΄μ€, μΌλ°₯ μΌμ΄μ€λ‘ μμ±ν μ μμ΅λλ€.
[1-1] μ μ, μ§μ λ±λ‘
μ μ λ±λ‘μ μμ£Ό μ¬μ©λκ±°λ μΈμ λ μ§ μ¬μ©ν μ»΄ν¬λνΈλ€λ§ λ±λ‘ν©λλ€.
import { createApp } from "vue";
import App from "./App.vue";
import Btn from "./components/Btn";
const app = createApp(App);
app.component("Btn", Btn); // μ μ λ±λ‘
app.mount("#app");
λ°λΌμ λλΆλΆμ μ»΄ν¬λνΈλ μ§μ λ±λ‘μ ν΅ν΄ μ¬μ©ν©λλ€.
import Hello from "~/components/Hello";
import Btn from "~/components/Btn";
export default {
components: {
Hello,
Btn, // μ§μ λ±λ‘
},
// ...
[2] μ»΄ν¬λνΈ Props
props
λ λ°μ΄ν° νμ
μ λͺ
μν΄μ€ μ μμ΅λλ€.
μ°Έκ³ λ‘ JavaScriptμμ μμ±ν props λͺ μ΄ μΉ΄λ© μΌμ΄μ€μ¬λ, htmlμμμλ μΌλ°₯ μΌμ΄μ€λ‘ μμ±ν΄μΌ μΈμλ©λλ€.
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // λλ λ€λ₯Έ μμ±μ
}
:(v-bind λλ ν°λΈ)
λ₯Ό μ¬μ©νμ¬ λμ μΈ λ°μ΄ν°λ₯Ό μ λ¬ν μ μμ΅λλ€.
<!-- λ³μμ κ°μ λμ μΌλ‘ ν λΉ -->
<blog-post :title="post.title"></blog-post>
<!-- 볡μ‘ν ννμ ν¬ν¨ν λμ κ° ν λΉ -->
<blog-post :title="post.title + ' by ' + post.author.name"></blog-post>
λν μ΄κΈ°κ°μ μ§μ ν΄μ€ μ μμ΅λλ€.
<Hello :message="msg" name="Hello!" />
export default {
props: {
message: String, // μ΄λ { type: String, default: μ΄κΈ°κ° }μ μ§μ ν΄μ€ μ μμ΅λλ€.
name: String,
},
// ...
[2-1] Props μ ν¨μ± κ²μ¬
app.component("my-component", {
props: {
// κΈ°λ³Έ νμ
μ²΄ν¬ (`null`κ³Ό `undefined`λ λͺ¨λ νμ
μ ν¨μ± κ²μ¬λ₯Ό ν΅κ³Όν©λλ€.)
propA: Number,
// μ¬λ¬ νμ
νμ©
propB: [String, Number],
// λ¬Έμμ΄ νμ
propC: {
type: String,
required: true,
},
// κΈ°λ³Έ κ°μ κ°λ μ«μν
propD: {
type: Number,
default: 100,
},
// κΈ°λ³Έ κ°μ κ°λ κ°μ²΄ νμ
propE: {
type: Object,
// κ°μ²΄λ λ°°μ΄μ κΈ°λ³Έ κ°μ νμ ν©ν 리 ν¨μλ‘λΆν° λ°νλμ΄μΌ ν©λλ€.
default: function () {
return { message: "hello" };
},
},
// 컀μ€ν
μ ν¨μ± κ²μ¬ ν¨μ
propF: {
validator: function (value) {
// κ°μ΄ κΌ μλ μΈ λ¬Έμμ΄ μ€ νλμ μΌμΉν΄μΌ ν©λλ€.
return ["success", "warning", "danger"].indexOf(value) !== -1;
},
},
// κΈ°λ³Έ κ°μ κ°λ ν¨μ
propG: {
type: Function,
// κ°μ²΄λ λ°°μ΄κ³Ό λ¬λ¦¬ μλ ννμ ν©ν 리 ν¨μκ° μλλλ€. κΈ°λ³Έ κ°μΌλ‘ μ¬μ©λλ ν¨μμ
λλ€.
default: function () {
return "Default function";
},
},
},
});
[3] μ»΄ν¬λνΈ Non-Prop μμ±
$attrs
νλ‘νΌν°λ₯Ό ν΅ν΄ propsκ° μλ Non-Prop μμ±λ€μ μ κ·Όν μ μμ΅λλ€.
<!-- App.vue(μμ μ»΄ν¬λνΈ) -->
<Hello class="hello" style="font-size: 100px" @click="msg += '!'" />
<!-- Hello.vue(νμ μ»΄ν¬λνΈ) -->
<h1 v-bind="$attrs">Hello</h1>
Non-Prop μμ±λ€μ΄ μλμΌλ‘ λ£¨νΈ μμμ μμλλ κ²μ μμΉ μμ λ, inheritAttrs
μμ±μ μ¬μ©ν μ μμ΅λλ€.
export default {
inheritAttrs: false, // falseλ‘ μ€μ νλ©΄ μμμ΄ μΌμ΄λμ§ μμ΅λλ€.
props: {
style: Object,
},
mounted() {
console.log(this.$attrs);
},
};
[4] μ»΄ν¬λνΈ μ»€μ€ν μ΄λ²€νΈ
@ λλ ν°λΈ
μ emit
μ μ¬μ©ν΄μ 컀μ€ν
μ΄λ²€νΈλ₯Ό μ μνκ³ μ¬μ©ν μ μμ΅λλ€.
<!-- App.vue(μμ μ»΄ν¬λνΈ) -->
<template>
<h1>{{ msg }}</h1>
<Hello @add="msg += '?!'" />
</template>
<!-- Hello.vue(νμ μ»΄ν¬λνΈ) -->
<template>
<h1 @click="$emit('add')">Hello</h1>
</template>
<script>
export default {
emits: ["add"],
};
</script>
λ€λ§ λ€μ΄ν°λΈ μ΄λ²€νΈλ₯Ό μ£Όμν΄μΌ ν©λλ€.
clickμ΄λΌλ μλ°μ€ν¬λ¦½νΈ λ€μ΄ν°λΈ μ΄λ²€νΈλ₯Ό 컀μ€ν μ΄λ²€νΈλ‘ μ μΈν μ μμ΅λλ€.
export default {
emits: ["click"],
};
μ΄λ κ² λλ©΄ μλ°μ€ν¬λ¦½νΈμ λ΄μ₯λ ν΄λ¦ μ΄λ²€νΈλ λμνμ§ μμ΅λλ€.
[5] μ»΄ν¬λνΈ Slots
slot
νκ·Έλ₯Ό μ¬μ©νμ¬ μμ μ»΄ν¬λνΈμμ μ μΈν νμ μ»΄ν¬λνΈ μ¬μ΄μ Contentλ₯Ό λ λλ§ν μ μμ΅λλ€.
<!-- App.vue(μμ μ»΄ν¬λνΈ) -->
<template>
<h1>{{ msg }}</h1>
<Hello>Hello Compnent</Hello>
<!-- ν΄λΉ λ΄μ©μ΄ νμ μ»΄ν¬λνΈ slotμ λ€μ΄κ°λλ€. -->
</template>
<!-- Hello.vue(νμ μ»΄ν¬λνΈ) -->
<template>
<h1>Hello!</h1>
<slot></slot>
</template>
[5-1] Fallback Content
ν΄λΉ slot νκ·Έ λ΄λΆμ Fallback Content
λ₯Ό μμ±ν μ μμ΅λλ€.
μμ μ»΄ν¬λνΈμ νμ μ»΄ν¬λνΈ νκ·Έ λ΄λΆμ contentκ° μμ κ²½μ°μ μμ±λλ contentλ₯Ό Fallback content
λΌ ν©λλ€.
<!-- App.vue(μμ μ»΄ν¬λνΈ) -->
<template>
<h1>{{ msg }}</h1>
<Hello>Hello Compnent</Hello>
<Btn></Btn>
</template>
<!-- Btn.vue(νμ μ»΄ν¬λνΈ) -->
<template>
<button>
<slot>Click me!</slot>
<!-- μμ μ»΄ν¬λνΈμ contentκ° μμΌλ―λ‘ ν΄λΉ contentκ° λ³΄μ¬μ§λλ€. -->
</button>
</template>
[5-2] v-slot
v-slot
λλ ν°λΈλ₯Ό μ΄μ©νμ¬ μνλ μ¬λ‘―μ ν΄λΉ ν
νλ¦Ώ λ΄μ©μ μΆκ°ν μ μμ΅λλ€.
v-slot
λλ ν°λΈλ #
μ μ½μ΄λ₯Ό μ¬μ©ν μ μμ΅λλ€.
<!-- App.vue(μμ μ»΄ν¬λνΈ) -->
<template>
<h1>{{ msg }}</h1>
<Hello>Hello Compnent</Hello>
<Btn>
<template v-slot:buttonSlot>
<h2>ABC</h2>
</template>
</Btn>
</template>
<!-- Btn.vue(νμ μ»΄ν¬λνΈ) -->
<template>
<button>
<slot name="buttonSlot">Click me!</slot>
</button>
</template>
μ΄λμ slot μ’ λ₯λ λ κ°μ§λ‘ λλμ΄μ§λλ€.
[1] name slot
μ΄λ κ² name μμ±μ κ°μ§ slotμ nameμ κ°μ§λ slotμ΄λΌκ³ ν©λλ€.
<slot name="buttonSlot">Click me!</slot>
μμ μ»΄ν¬λνΈμμ v-slot
λλ ν°λΈλ‘ μ§μ μ κ·Όμ΄ κ°λ₯ν©λλ€.
<Btn>
<template v-slot:buttonSlot>
<h2>ABC</h2>
</template>
</Btn>
[2] λ²μλ₯Ό κ°μ§λ μ¬λ‘―
slotμ λ²μλ₯Ό κ°μ§ μ μμ΅λλ€.
λ²μλ ν΄λΉ μ¬λ‘―μ΄ μ¬μ©λλ ꡬκ°μ λ»ν©λλ€.
<slot :hello="123"></slot>
<!--
μ΄λ κ² nameμ μ§μ ν΄μ£Όμ§ μμΌλ©΄ #defaultλ‘ μ κ·Όν μ μμΌλ©°
default μ¬λ‘―μ΄λΌ ν©λλ€.
-->
λμΌνκ² v-slot
λλ ν°λΈλ‘ ν΄λΉ μ¬λ‘― λ΄λΆμ λ²μμ μ κ·Όν μ μμ΅λλ€.
<Hello>
<template #default="{ hello }">
<!-- ν΄λΉ hello λ°μ΄ν°μ μ κ·Όν μ μμ΅λλ€. -->
<h2>{{ hello }}</h2>
</template>
</Hello>
ν΄λΉ name μμ±κ³Ό λ°μ΄ν°λ λμ μΌλ‘ μ μ΄ν μ μμ΅λλ€.
[6] λμ μ»΄ν¬λνΈ
νμμ λ°λΌ νΉμ μ»΄ν¬λνΈλ₯Ό κ΅μ²΄ν΄μ€ μ μλ λμ μ»΄ν¬λνΈμ κ°λ μ μ¬μ©ν μ μμ΅λλ€.
<component :is="currendComponent"></component>
<!--
κ΅μ²΄λ μ μλ λͺ¨λ μ»΄ν¬λνΈλ€μ is μμ±μ μ§μ ν©λλ€
λ³΄ν΅ μ¬λ¬κ°μ μ»΄ν¬λνΈκ° κ΅μ λλ―λ‘, dataλ₯Ό λ£μ΄μ€λλ€.
-->
λμ μ»΄ν¬λνΈλ μμ£Ό μ νλ λλ μ¬μ©νμ§ μλ κ²μ΄ μ’μ΅λλ€.
μ ν λΉμ©μ΄ λκΈ° λλ¬Έμ λλ€.
μ΄λ keep-alive
νκ·Έλ₯Ό μ¬μ©νμ¬ μΊμ± κΈ°λ₯μ μ¬μ©ν μ μμ΅λλ€.
<template>
<h1 @click="currentComponent = 'World'">{{ msg }}</h1>
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
</template>
λμ μ ν λΉμ©μ μΊμ± κΈ°λ₯μ μ¬μ©νμ¬ ν΄κ²°ν΄μ€ μ μκΈ° λλ¬Έμ
λμ μ»΄ν¬λνΈ κ°λ
μ μ¬μ©ν λ, ν΄λΉ keep-alive
νκ·Έλ‘ λ¬Άμ΄μ£Όλ κ²μ΄ κΆμ₯λ©λλ€.
[7] Refs
λ³΄ν΅ μ΄λ ν html νΉμ μ리먼νΈλ₯Ό μ°ΎκΈ° μν΄ querySeletor
μ μ¬μ©ν©λλ€.
νμ§λ§ querySeletor
μ ν΄λΉ μ»΄ν¬λνΈμ μ 체 documentλ₯Ό μ€μΊνμ¬ μνλ μμλ₯Ό μ°Ύμμ΅λλ€.
λ°λΌμ ν¨μ¨μ±μ μν΄ ref
μμ±μ μ¬μ©ν μ μμ΅λλ€.
ref
μμ±μ referenceμ μ€μλ§μ΄μ html νΉμ μ리먼νΈλ₯Ό μ°Έμ‘°ν μ μλ μμ±μ
λλ€.
<h1 ref="hello">Hello</h1>
<!-- μ»΄ν¬λνΈμ ref μμ±μ λΆμ¬λ μ΄λ¦μΌλ‘ μ κ·Όν©λλ€. -->
const $h1El = this.$refs.hello; // μ°Έμ‘°ν΄μ μ¬μ©ν μ μμ΅λλ€.
console.log($h1El);
ν΄λΉ ref
μμ±μ html μμκ° μ°κ²°λ μ΄νλΆν° μ¬μ©ν μ μμ΅λλ€. (mount)
νΉμ component νκ·Έμ μ§μ μ μ©λ κ°λ₯ν©λλ€.
λ€λ§ component λ΄λΆ μμμ μ§μ μ κ·ΌνκΈ° μν΄μλ $el
μ μΆκ°ν΄μΌ ν©λλ€.
const $componentEl = this.$refs.hello.$el;
μ΄μΈμλ λ€μν componentμ λ΄μ₯λ μμ±μ μ κ·Όν μ μμ΅λλ€.
λ°λΌμ refλ‘ μ κ·Όν html μμμ μμ± λν μ κ·Όν΄μ μ μ₯ν μ μμ΅λλ€.
$refs
λ μ€μ²©μ΄ κ°λ₯ν©λλ€.
[+] $nextTick
λ³΄ν΅ λ°μ΄ν°κ° λ³κ²½λ μ΄νμλ λ°λ‘ μμκ° λ λλ§λμ§ μμ΅λλ€.
λ°μ΄ν°κ° λ³κ²½λλ ν¨μ μμ λ‘μ§μ΄ μ΄λμ λ μλ£λ ν, λ λλ§ μμ μ΄ μΌμ΄λ©λλ€.
λ°λΌμ λ°μ΄ν°κ° λ³κ²½λ μ΄νμ νΉμ μμμ μ κ·Όν΄μ λ λλ§ μμ±μ μ‘°μν΄λ
ν΄λΉ μ‘°μμ΄ μ μμ μΌλ‘ λμνμ§ μμ λκ° λ§μ΅λλ€.
μ΄λ, λ°μ΄ν°μ λ³κ²½μ΄ μλ£λκ³ λ λλ§(DOM μ
λ°μ΄νΈ)μ μ²λ¦¬ν΄μ£Όλ $nextTick
μ μ¬μ©ν μ μμ΅λλ€.
onEdit() {
this.data = true;
this.$nextTick(() => {
this.$refs.editor.focus()
});
}
$refs
μ$nextTick
μ λλΆλΆ κ°μ΄ μ¬μ©λ©λλ€.
λκΈλ¨κΈ°κΈ°