[DAY-34] Vue (1)
π ν·κ°λ Έλ & λͺ°λλ λΆλΆλ€λ§ μ 리νλ λλ§μ TIL
π― λͺ¨λ κ°μ λ΄μ©μ μ μ§ μμμ!
μ€λμ μκ°μ?
ν μ΄ νλ‘μ νΈ λ μ²μ λ§λ¬λ Vue..
λ°λΈ μ½μ€κ° λ°λΉ μκ³ μ΄μλλ° λ€μ λ§λκ² λ μ€μ λͺ°λμ΄μ.
μκ³ μ° λ§νΌ..λ€ κΉλ¨ΉμλλΌκ³ μ...
μ°λ¦¬ λ€μ μμνμ
[1] VueJS
Vue.jsλ μ¬μ©μ μΈν°νμ΄μ€λ₯Ό λ§λ€κΈ° μν νλ‘κ·Έλ μλΈ νλμμν¬μ λλ€.
[2] μ μΈμ λ λλ§(Declarative Rendering)
μ μΈμ λ λλ§μ Vueμ μ€μν νΉμ§μ λλ€.
κ°λ¨ν ν νλ¦Ώ ꡬ문μ μ¬μ©νμ¬ DOMμμ λ°μ΄ν°λ₯Ό μ μΈμ μΌλ‘ λ λλ§ν μ μμ΅λλ€.
<div id="counter">Counter: {{ counter }}</div>
const Counter = {
data() {
return {
counter: 0,
}; // μ΄μ€ μ€κ΄νΈ ꡬ문
},
};
Vue.createApp(Counter).mount("#counter");
counter λ°μ΄ν°μ html DOMμ΄ μ°κ²°λμμΌλ©°, μ΄λ₯Ό λ°μν(reactive)
μ΄ λμλ€κ³ λ§ν μ μμ΅λλ€.
λ§μ½ counter λ°μ΄ν°κ° 맀 μ΄λ§λ€ μ¦κ°νκ³ html DOMμ΄ λ³κ²½λ λ μ΄λ»κ² λ λλ§λ κΉμ?
const Counter = {
data() {
return {
counter: 0,
}; // μ΄μ€ μ€κ΄νΈ ꡬ문
},
mounted() {
setInterval(() => {
this.counter++;
}, 1000);
},
};
Vue.createApp(Counter).mount("#counter");
μ΄λ―Έ ν΄λΉ λ°μ΄ν°λ html DOMκ³Ό μ°κ²°λμμ΅λλ€.
λ°λΌμ λ°μ΄ν°κ° λ³κ²½λ μ, html μ΄μ€ μ€κ΄νΈ ꡬ문 λ΄μ λ³μμ κ°λ λ³κ²½λμ΄ λ λλ§λλ κ²μ νμΈν μ μμ΅λλ€.
κ³μν΄μ μ¦κ°ν©λλ€.
- setInterval Func
μ΄λ, setInterval ν¨μλ νμ΄ν ν¨μλ‘ μμ±λμ΄μΌ ν©λλ€.
μΌλ° ν¨μμμμ thisλ ν¨μκ° μ€ν(νΈμΆ)λλ μμΉμμ μ μκ° λ©λλ€.
setInterval(function () {
this.counter += 1;
// μ¬κΈ°μ μ¬μ©νλ thisλ function λ΄λΆμμ μ μλλ―λ‘, μΈλΆμ κ°μ μ°Έμ‘°ν μ μμ΅λλ€.
}, 1000);
νμ΄ν ν¨μμ κ²½μ° ν¨μκ° μ μΈλλ μμΉμμ μ μκ° λκΈ° λλ¬Έμ, ν¨μ λ΄λΆκ° μλ μ μΈλλ μμΉμ μλ κ°μ μ°Έμ‘°ν μ μμ΅λλ€.
setInterval(() => {
this.counter++;
}, 1000);
- component
componentλ λ°μ΄ν°μ μΊ‘μνλ₯Ό μ§μν©λλ€. (νλ¨μ ννΈμμ λ λ€λ£¨λλ‘ νκ² μ΅λλ€. )
λν v-forμ μ¬μ©νμ¬ λ°λ³΅ μΆλ ₯μ ν μ μμ΅λλ€.
<!-- 3. μΉ΄λ© μΌμ΄μ€ -> μΌλ°₯ μΌμ΄μ€λ‘ λ°κΎΈμ΄ μμ± -->
<!-- 4. μ¬μ©ν μ»΄ν¬λνΈ λ€μ΄λ°μΌλ‘ νκ·Έ μμ± -->
<!-- 5. μ¬μ©ν μ»΄ν¬λνΈ λ΄λΆμ λ°μ΄ν° νλ‘μ€μ λ€μ΄κ° κ° μ μ -->
<fruit-item fruit-name="Orange"></fruit-item>
// 1. componentλ‘ μ¬μ©ν κ°μ²΄ νμ
μ μ
const FruitItem = {
template: "<li>{{ fruitName }}</li>",
props: ["fruitName"],
};
// 2. components μμμ ν΄λΉ λ°μ΄ν° μ¬μ©ν κ² λͺ
μ
const App = {
components: {
FruitItem,
},
data() {
return {
fruits: ["Apple", "Banana", "Cherry"],
};
},
};
fruit-name νλ‘μ€ κ°μΌλ‘ μ λ¬λ orange λ¬Έμμ΄μ΄ ν΄λΉ μ»΄ν¬λνΈμ templateμ μ μ©λμ΄ λ λλ§ λ κ²μ νμΈν μ μμ΅λλ€.
κ·Έλ λ€λ©΄ v-for
μ μ¬μ©νμ¬ fruits λ°μ΄ν°λ₯Ό λ£μ΄λ³΄λλ‘ νκ² μ΅λλ€.
<fruit-item v-for="f in fruits" v-bind:fruit-name="f"></fruit-item>
μ΄λ v-for
λ‘ μνλ λ°μ΄ν° fλ₯Ό μ¬μ©ν λ v-bind
λλ ν°λΈλ₯Ό λΆμ¬μ€μΌ μ¬μ©ν μ μμ΅λλ€.
λ°λΌμ λλ ν°λΈλ₯Ό μ¬μ©ν¨μΌλ‘μ¨, JS ꡬ문과 DOM μμλ€μ μνΈμμ©μ λ§λ€ μ μμ΅λλ€.
[2-2] Component
Component
λ 미리 μ μλ μ΅μ
μ κ°μ§ Vue μΈμ€ν΄μ€λ₯Ό λ§ν©λλ€.
μ»΄ν¬λνΈ μμ€ν μ Vueμ μ€μν κ°λ μ λλ€.
λ°μ΄ν°μ μΊ‘μνλ₯Ό μ§μνλ©°, μ¬μ¬μ©ν μ μλ μμ λ¨μλ‘ λ§λ€ μ μκΈ° λλ¬Έμ
λκ·λͺ¨ μ΄ν리μΌμ΄μ μ ꡬμΆν μ μκ² ν΄μ£Όλ μΆμμ κ°λ μ λλ€.
μ μ, μ§μ μ μΈ λ°©λ²μ μ»΄ν¬λνΈ μ μΈ λ°©λ² ννΈμμ λ€λ£Ήλλ€.
[3] λΌμ΄ν μ¬μ΄ν΄
κ° μ»΄ν¬λνΈλ μμ±λ λ μΌλ ¨μ μ΄κΈ°ν κ³Όμ μ κ±°μΉ©λλ€.
(λ°μ΄ν° κ΄μ°°, ν νλ¦Ώ μ»΄νμΌ, μΈμ€ν΄μ€ DOM λ§μ΄νΈ(μ°κ²°), λ°μ΄ν° λ³κ²½ μ DOM μ λ°μ΄νΈ)
μ΄ κ³Όμ μμ λΌμ΄νμ¬μ΄ν΄ ν ν¨μλ₯Ό μ€νν μ μμ΅λλ€.
[3-1] λΌμ΄νμ¬μ΄ν΄ λ€μ΄μ΄κ·Έλ¨
beforeCreate() {
// λ§λ€μ΄μ§κΈ° μ
console.log("before Create!", this.msg);
console.log(document.querySelector("h1"));
},
created() {
// λ§λ€μ΄μ§ μ§ν
console.log("created!", this.msg);
console.log(document.querySelector("h1"));
},
beforeMount() {
// DOM element μ°κ²°λκΈ° μ§μ
console.log("before Mount!", this.msg);
console.log(document.querySelector("h1"));
},
mounted() {
// DOM element μ°κ²°λ μ§ν
console.log("mounted!", this.msg);
console.log(document.querySelector("h1"));
},
beforeUpdate() {
// λ°μ΄ν°κ° λ³κ²½λ ν λ λλ§λ μ
λ°μ΄νΈ λκΈ° μ
console.log("before update!", this.msg);
console.log(document.querySelector("h1").textContent);
},
updated() {
// λ°μ΄ν°μ λ λλ§ λͺ¨λ λ³κ²½μ¬νμ΄ λ°μλ μ§ν
console.log("updated!", this.msg);
console.log(document.querySelector("h1").textContent);
},
beforeUnmount() {
// DOM elementμ μ°κ²°μ΄ λκΈ°κΈ° μ§μ
console.log("before unmount");
},
unmounted() {
// DOM elementμ μ°κ²°μ΄ λμ΄μ§ ν
console.log("unmounted!");
},
[4] ν νλ¦Ώ λ¬Έλ²
[4-1] 보κ°λ² (Interpolation)
μ΄μ€ μ€κ΄νΈ ꡬ문(Mustache) κΈ°λ²μ μ¬μ©ν λ¬Έμμ΄ λ³΄κ°λ²μ μ¬μ©ν μ μμ΅λλ€.
<p>λ©μΈμ§: {{ message }}</p>
ν΄λΉ μ΄μ€ μ€κ΄νΈ ꡬ문μ μ»΄ν¬λνΈ μΈμ€ν΄μ€μ message μμ± κ°μΌλ‘ λ체λλ©°, μμ± κ°μ΄ λ³κ²½λ λλ§λ€ κ°±μ λ©λλ€.
ν΄λΉ μ΄μ€ μ€κ΄νΈ ꡬ문 λ΄μμλ κ°λ¨ν JavaScript
ννμμ μ§μν©λλ€.
λ¨, λ¨μΌ ννμλ§ μ§μν©λλ€.
<h1 v-bind:class="name">{{ name + 'μ
λλ€' }}</h1>
<h1>{{ count + 100 }}</h1>
- v-once
μ΄λ v-once
λλ ν°λΈλ₯Ό μ¬μ©νμ¬ λ°μ΄ν°κ° λ³κ²½λμ΄λ κ°±μ νμ§ μλ μΌνμ± λ³΄κ°μ μμ±ν μ μμ΅λλ€.
<p v-once>{{ message }}</p>
- v-html
λ§μ½ μ΄μ€ μ€κ΄νΈ ꡬ문 μμ htmlμ μΆλ ₯νκ³ μΆλ€λ©΄ v-html
λλ ν°λΈλ₯Ό μ¬μ©νμ¬ μΆλ ₯ν μ μμ΅λλ€.
<div id="app">
<h1 v-html="msg">{{ msg }}</h1>
</div>
const App = {
data() {
return {
msg: "<p style='color:red'>hello Vue!</p>",
};
},
};
Vue.createApp(App).mount("#app");
html μ½λκ° μ μ μ©λ κ²μ νμΈν μ μμ΅λλ€.
νμ§λ§ μμμ HTMLμ λμ μΌλ‘ λ λλ§ ν κ²½μ°, XSSμ μνμ΄ μμΌλ―λ‘ μ£Όμν΄μ μ¬μ©ν΄μΌν©λλ€.
[4-2] λλ ν°λΈ
λλ ν°λΈλ Vueμμ μ 곡νλ νΉμ μμ±λ€μ λλ€.
μ΄ μμ±λ€μ v-
μ λμ΄κ° λΆμ΄μμΌλ©°, λ λλ§ λ DOMμ΄ λ°μν λμμ νλλ‘ λμμ€λλ€.
βΌ κ°λ¨ν λλ ν°λΈλ€λ§ λ€λ£¬ ν, κ° μμΈν λ΄μ©μ νμ λ€λ£Ήλλ€.
[1] v-bind
JavaScriptμ κ°μ²΄λ₯Ό μμ±μ κ°μΌλ‘ λ£κ³ μΆμ λ, v-bind λλ ν°λΈλ₯Ό μ¬μ©ν μ μμ΅λλ€.
<div v-bind:class="{ orange: active }">{{ counter }}</div>
ν΄λΉ λλ ν°λΈλ₯Ό μ¬μ©νλ©΄ μμ± κ°μΌλ‘ λ£μ JS λ°μ΄ν° ννκ° νλμ μμ± κ°μΌλ‘ μ½ν μ μμ΅λλ€.
λν λ΄λΆμμ data()μ μ μν΄λμ κ°λ€κ³Ό μ°κ²°ν΄μ μ¬μ©ν μ μμ΅λλ€.
ν΄λΉ λλ ν°λΈλ μ½μ΄λ‘ μμ±ν μ μμ΅λλ€.
<a v-bind:href="url"> ... </a>
<a :href="url"> ... </a>
<a : [key]="url"> ... </a>
<!-- λμ μ λ¬μΈμμ ν¨κ» μ΄ μ½μ΄ -->
[2] v-on
v-on λλ ν°λΈλ₯Ό μ¬μ©νμ¬ htmlμμ μ΄λ²€νΈ ν¨λ€λ¬λ₯Ό μΆκ°ν μ μμ΅λλ€.
<button v-on:click="increase">Click me!</button>
const App = {
data() {
return {
counter: 0,
active: false,
};
},
methods: {
increase() {
this.counter += 1;
},
},
};
ν΄λΉ λλ ν°λΈλ μ½μ΄λ‘ μμ±ν μ μμ΅λλ€.
<a v-on:click="doSomething"> ... </a>
<a @click="doSomething"> ... </a>
<a @[event]="doSomething"> ... </a>
<!-- λμ μ λ¬μΈμμ ν¨κ» μ΄ μ½μ΄ -->
[3] v-if
v-if λλ ν°λΈλ₯Ό ν΅νμ¬ λ§μΉ μμ±μ μμ±νλ―μ΄ λ λλ§ μ¬λΆλ₯Ό κ²°μ ν΄μ£Όλ λ°μ΄ν°λ₯Ό μ μν μ μμ΅λλ€.
<div v-if="active">Hello Vue!</div>
const App = {
data() {
return {
active: false,
};
},
methods: {
toggle() {
this.active = !this.active;
},
},
};
toggle μ΄λ²€νΈκ° λ°μνμ¬ active κ°μ΄ λ³ν¨μ λ°λΌ μμ div λ λλ§ μ¬λΆκ° κ²°μ λ©λλ€.
[4] v-for
v-for λλ ν°λΈλ₯Ό ν΅νμ¬ JSμ μ΄ν°λ¬λΈ λ°μ΄ν°λ₯Ό μνν μ μμ΅λλ€.
<div id="app">
<ul>
<li v-for="f in fruits">{{f}}</li>
<!-- fλ μΌμ’
μ 맀κ°λ³μμ
λλ€. -->
</ul>
</div>
const App = {
data() {
return {
fruits: ["Apple", "Banana", "Cherry"],
};
},
};
Vue.createApp(App).mount("#app");
fruits dataκ° μνλμ΄ liμ μ λ ₯λ κ²μ νμΈν μ μμ΅λλ€.
[5] λμ λ°μ΄ν°
λλ ν°λΈμ μμ± μ΄λ¦λ λμ μΌλ‘ μμ±ν μ μμ΅λλ€.
<h1 v-bind:[attr]="'active'">Hello vue!</h1>
const App = {
data() {
return {
attr: "class",
};
},
};
const vm = Vue.createApp(App).mount("#app");
[5] Data & Methods
[5-1] Data
Vueμμ λμ λ°μ΄ν°μ μ κ·Όν λλ $data
λΌλ λ΄μ₯ κ°μ²΄μ μ κ·Όν΄μ νμΈν μ μμ΅λλ€.
vm.$data.count; // 7
vm.count; // 7
μΌλ°μ μΌλ‘ $data
λ₯Ό μ΄μ©ν΄μ μ κ·Όνλ κ²μ΄ κΆμ₯λ©λλ€.
[5-2] Proxy
Proxy
κ°μ²΄λ κΈ°λ³Έμ μΈ λμ(μμ± μ κ·Ό, ν λΉ, μν, μ΄κ±°, ν¨μ νΈμΆ λ±)μ μλ‘μ΄ νλμ μ μν λ μ¬μ©ν©λλ€.
new Proxy(target, handler);
// target : κ°μν λ°μ΄ν°
// handler : getter, setterμ κ°μ§λ©°, λ°μ΄ν°λ₯Ό κ°μ Έμ€κ³ μ‘°μν ν λ°νν μ μμ΅λλ€.
Proxy
λ λ°μ΄ν°μ λ³κ²½μ κ°μ§ν΄μ νλ©΄μ μΆλ ₯νλ λ‘μ§κΉμ§ λ΄λΉν©λλ€.
λ°λΌμ μλ‘μ΄ Proxy
λ₯Ό μ μΈν ν μνλ λ‘μ§μ μ
λ ₯νμ¬ λ λλ§κΉμ§μ κ³Όμ μ κ°μ
ν μ μμ΅λλ€.
[5-3] Methods
μ»΄ν¬λνΈ μΈμ€ν΄μ€μ ν¨μλ₯Ό μΆκ°νκΈ° μν΄ methods
λ₯Ό μ¬μ©ν μ μμ΅λλ€.
const App = {
data() {
return {
count: 0,
};
},
methods: {
increase() {
this.count += 1; // thisκ°μ μλμΌλ‘ λ°μΈλ©λ©λλ€.
},
},
};
methods
λ₯Ό μ μΈν λ, νμ΄ν ν¨μλ₯Ό μ¬μ©νλ©΄ Vueκ° μ μ ν thisκ°μ λ°μΈλ©νμ§ λͺ»ν©λλ€.
λ°λΌμ νμ΄ν ν¨μλ₯Ό μ¬μ©νμ§ μλλ‘ ν©λλ€.
methodsλ μΌλ°μ μΌλ‘ ν νλ¦Ώ λ΄λΆμμ μ΄λ²€νΈ 리μ€λλ‘ νμ©λ©λλ€.
<button @click="increase">count ++</button>
<!-- v-onμ μ½μ΄λ @ μ
λλ€. -->
λκΈλ¨κΈ°κΈ°