[DAY 33] Vue (1)
📘 Vue 란?
: 사용자 인터페이스를 구축하기 위한 JavaScript 프레임워크 중 하나
- 선언적 렌더링(Declarative Rendering): Vue는 표준 HTML에서 JavaScript 상태(State)를 기반으로 화면에 출력될 HTML을 선언적으로 작성 가능
- 반응성(Reactivity): 상태가 변경되면 DOM(화면)을 가상 DOM을 이용하여 효율적으로 업데이트함
1. 시작하기
- 본 강의에서는 CDN을 사용하였다.
<script src="https://unpkg.com/vue@3"></script>
1) 기본 문법
<div id="app">{{ message }}</div>
<script>
const { createApp } = Vue;
const app = createApp({
data() {
return {
message: 'Hello Vue!'
}
}
};
app.mount('#app')
</script>
✅ createApp()
- data는 Vue의 인스턴스가 사용할 정보를 담고있는 속성으로 객체/함수 형태이다.
- 위 예제에서는 message라는 변수를 선언했다.
✅ mount()
- 앱 인스턴스를 렌더링한다.
- mount()가 실행되기 전까진 아무것도 렌더링 되지 않는다.
✅ {{ }} (이중 괄호)
- data에서 선언했던 데이터들을 이중 괄호 형태로 선언하여 화면에 출력한다.
2) 템플릿 문법
: Vue는 컴포넌트 인스턴스의 데이터를 DOM에 바인딩할 수 있는 HTML 기반 템플릿 문법을 사용한다.
ⓐ 텍스트 보간법
- 가장 기본적인 형태로 이중 중괄호 문법을 이용한다.
<span>메세지: {{ msg }}</span>
- 이중 중괄호 내 msg는 해당 컴포넌트 인스턴스의 msg 속성 값으로 대체 된다.
- v-once 디렉티브르 사용하여 데이터가 변경되어도 갱신되지 않는 일회성 보간 수행 가능
한번만 보여지면 될때 (바뀔 필요가 없을때 사용) -메모리 절약가능
✅ 이 중 중괄호 내에서는 자바스크립트 표현식 사용이 가능하다.
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
- 하나의 단일 표현식만 포함될 수 있다.
- 함수 호출이 가능하다.
ⓑ 원시 html
- 이중 중괄호는 데이터를 HTML이 아닌 일반 텍스트로 해석하기 때문에 실제 HTML을 출력하려면 v-html을 사용
<p>텍스트 보간법 사용: {{ rawHtml }}</p>
<p>v-html 디렉티브 사용: <span v-html="rawHtml"></span></p>
내부 html로 들어가기 때문에 태그 중복 가능성이 있어 div로 선언 해주는 것이 좋음
<div id="app">
<div>{{ rawHTML }}</div>
<div v-html="rawHTML"></div>
</div>
<script>
const App = {
data() {
return {
rawHTML: '<h3 style="color:red;">Raw HTML</h3>',
};
},
};
const app = Vue.createApp(App);
const vm = app.mount("#app");
* xxs 공격에 주의해야한다.
ⓒ 속성 : v-bind
<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div> // 축약해서 사용가능
// 컴포넌트 형태
createApp({
data() {
return {
dynamicId: 'red'
}
}
}).mount('#app')
- 해당 요소 (id) 속성을 컴포넌트의 (dynamicId) 속성 - red 와 동기화된 상태로 유지한다
- 위 코드는 <div id="red"></div> 형태가 된다.
✅ 불리언 속성
- 참 거짓을 나타낼 수 있다.
<button :disabled="isButtonDisabled">Button</button>
- isButtonDisabled에 true/false 값을 바인딩하여 사용가능하다.
✅ 여러 속성을 동적으로 바인딩
<div id="app">
<h1 v-bind:[attr]="'active'">Hello Vue!</h1>
</div>
<script>
const App = {
data() {
return {
attr: "class",
};
},
};
const app = Vue.createApp(App);
const vm = app.mount("#app");
</script>
위 예시에서는 App 컴포넌트의 attr 속성을 h1의 속성으로 "class" 가 들어가서
실제 돔에서는 <h1 class="active"> 형태가 된다.
주의할 점은 active란 값을 넣기 위해서는 따옴표 안에 작은 따옴표를 넣어줘야 한다.
📌 v-bind: 약어 = 콜론기호 ( : 만써도됨)
* 자주 쓰이기 때문에 위와 같은 형태를 주로 쓴다.
2. Vue 생명주기
1) 전체 생명 주기
beforeCreate 👉 created 👉 beforeMount 👉 mounted 👉 beforeUnmount 👉 unmounted
① beforeCreate
createApp, mount 메서드로 element에 생성 하고, events와 lifecycle을 초기화 한 후(Init) 실행되는 주기
app = Vue.createApp(options)
app.mount(element);
② created
- 데이터의 주입과 반응성 구조를 파악한 후 실행되는 주기
- created 훅이 실행된 후, template 옵션이 있으면 템플릿을 컴파일하고, 없다면 내부 html을 템플릿 형태로 컴파일한다.
③ beforeMount
- 준비된 요소들을 html 요소들과 연결 렌더링할 준비 끝
④ mounted
- 연결 후 실행되는 훅
⑤ beforeUpdate(전), updated(후)
- 마운트 이후 실행되는 훅
- 가상돔이 있으면 다른 부분이 있으면(데이터가 바뀌면) 다른 부분만 다시 렌더링
⑥ beforeUnMount, unmount
- html 요소와 vue 내부 데이터 간 연결(반응성)이 끊어짐
✅ 라이프 사이클 예시
<div id="app">
<h1>{{ mag }}</h1>
</div>
<script>
const App = {
data() {
return {
mag: "안녕하세요",
};
},
beforeCreate() {
console.log("beforeCreate !", this.mag);
console.log(document.querySelector("h1"));
},
created() {
console.log("created !", this.mag);
console.log(document.querySelector("h1"));
},
beforeMount() {
console.log("beforeMount !", this.mag);
console.log(document.querySelector("h1"));
},
mounted() {
console.log("mounted !", this.mag);
console.log(document.querySelector("h1"));
},
};
const app = Vue.createApp(App);
const vm = app.mount("#app");
create 이전에는(beforeCreate) 내부 데이터와 연결이 안되어 있기때문에 this로 접근 불가능하다. (앱이 생성되지 않음)
mount되었을 때는 돔과 연결된 후이기 때문에 h1이 보이게 된다.
beforeUpdate() {
console.log("beforeUpdate !", this.mag);
console.log(document.querySelector("h1").textContent);
},
updated() {
console.log("updated !", this.mag);
console.log(document.querySelector("h1").textContent);
},
임의로 vue app의 data를 바꿨을 때 beforeUpdate에는 이전 data가 들어갈 것 같지만 그렇지 않다.
update의 의미는, 데이터가 업데이트 되기전 상태가 아니라 화면이 업데이트 되기 전 상태를 의미한다.
전 data를 가져오는 방법은 해당 요소의 textContent를 가져오면 된다.
beforeUnmount() {
console.log("beforeUnmount !", this.mag);
console.log(document.querySelector("h1").textContent);
},
unmounted() {
console.log("unmounted !", this.mag);
console.log(document.querySelector("h1").textContent);
},
- unmount되면 연결이 끊겨 화면이 보이지 않지만, 데이터 자체는 접근이 가능하다.
3. Data와 Methods
createApp으로 생성된 앱 인스턴스는 여러 data와 methods가 있다.
1) 앱 인스턴스 data 접근
data() {
return {
counter: 0,
};
},
데이터가 위와 같을 때, counter에 접근할 수 있는 방법은 아래와 같다.
vm.count
vm.$data.count
vm['count']
vm.$data['count']
2) 프록시
- Vue에서 createApp으로 생성된 인스턴스를 콘솔에 찍어보면 Proxy형태가 나온다.
* 프록시 : 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 새로운 행동을 정의할 때 사
✅ 사용자 Proxy 생성
const App = {
data() {
return {
count: 0,
};
},
};
// Proxy(감시할 타겟 데이터, 핸들러)
const proxyA = new Proxy(App.data, {
get(target, key) {
console.log("GETTER!", target, key);
return target[key];
},
set(target, key, value) {
console.log("SETTER!", target, key, value);
target[key] = value;
},
});
const vm = Vue.createApp(App).mount("#app");
- 내부 핸들러에 게터와 세터로 App 데이터에 접근하거나 바꿀 수 있다.
- 프록시를 여러개 생성 했을 때 원본 값에 영향을 주지 않으려면 매개변수에 App.data()형태로 넣어준다.
const proxyA = new Proxy(App.data(), {
...
});
3) methods
- methods 안에 원하는 커스텀 함수 작성하면 된다.
- 메서드를 정의할때는 화살표 함수를 이용하면 this가 달라짐 따라서 funciton 쓰는걸 권장함
* 디바운싱와 쓰로틀링은 제공하지 않는다.
<style>
.orange {
color: orange;
}
</style>
<div id="app">
<div v-bind:class="{ orange: active}">{{counter}}</div>
<button v-on:click="increase">click me</button>
</div>
<script>
const App = {
data() {
return {
counter: 0,
active: true,
};
},
methods: {
increase() {
this.counter += 1;
},
},
};
const app = Vue.createApp(App).mount("#app");
</script>