Vue는 다양한 Instance를 지원한다. 그 예시는 아래와 같다.
- Method : 특정 기능을 수행함
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<script>
let vm = new Vue({
data: {
count: 1,
},
created() {
console.log(this.count);
},
methods: {
incCount() {
console.log("incCount 호출됨");
this.count++;
},
},
});
vm.incCount();
console.log(vm.count); // 2
</script>
</body>
</html>
method 안에서 data에 접근하기 위해선 this를 사용해야 한다.
실행 결과는 아래와 같다.
- Filter : 텍스트 형식을 쉽게 변환함
filter는 전역 filter와 지역 filter로 나뉜다. 예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<input type="text" v-model="msg" />
</div>
<div>
<h2>결과 :</h2>
<h3>{{ msg | count1 }}</h3>
<h3>{{ msg | count2('문자를 넣어보세요') }}</h3>
</div>
</div>
<script>
// 전역 필터
Vue.filter('count1', (val) => {
if (val.length == 0) {
return;
}
return `${val} : ${val.length}자`;
});
new Vue({
el: '#app',
data: {
msg: '',
},
// 지역 필터
filters: {
count2(val, alternative) {
if (val.length == 0) {
return alternative;
}
return `${val} : ${val.length}자`;
},
},
});
</script>
</body>
</html>
{{ msg | count1 }}
에서 count1의 매개 변수인 val가 msg가 된다.
실행 결과는 아래와 같다.
- Computed : 특정 데이터를 변경이 없을 경우 캐싱해 사용하다가 데이터가 변경되면 이를 반영함
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="message" />
<p>원본 메시지: "{{ message }}"</p>
<p>역순으로 표시한 메시지1: "{{ reversedMsg }}"</p>
<p>역순으로 표시한 메시지2: "{{ reversedMsg }}"</p>
<p>역순으로 표시한 메시지3: "{{ reversedMsg }}"</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: '안녕하세요 여러분',
},
computed: {
reversedMsg: function () {
console.log('꺼꾸로 찍기');
return this.message.split('').reverse().join('');
},
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
만약 Computed를 Method로 쓰면 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="message" />
<p>원본 메시지: "{{ message }}"</p>
<p>역순으로 표시한 메시지1: "{{ reversedMsg() }}"</p>
<p>역순으로 표시한 메시지2: "{{ reversedMsg() }}"</p>
<p>역순으로 표시한 메시지3: "{{ reversedMsg() }}"</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: '안녕하세요 여러분',
},
methods: {
reversedMsg: function () {
console.log('꺼꾸로 찍기');
return this.message.split('').reverse().join('');
},
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
Computed와 달리 함수가 여러번 호출됨을 알 수 있다.
- Watch : 특정 데이터가 변경될 경우 다른 데이터에 이를 반영함
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<p>원본 메시지: "{{ message }}"</p>
<p>역순으로 표시한 메시지: "{{ reversedMsg }}"</p>
<input type="text" v-model="message" />
</div>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello',
reversedMsg: '',
},
watch: {
message: function (newVal, oldVal) {
console.log(newVal, oldVal);
this.reversedMsg = newVal.split('').reverse().join('');
},
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
Vue는 다양한 Event를 지원한다.
종류는 아래 3가지가 있다. 하나씩 살펴보자.
- v-on directive
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<button v-on:click="greet">Greet</button>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
name: "SAMSUNG",
},
methods: {
greet: function (event) {
alert("Hello " + this.name + "!");
console.dir(event.target);
},
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
- inline method handler
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<form action="http://www.naver.com">
<button v-on:click="greet1('SAMSUNG')">Greet</button>
<button v-on:click="greet2($event, 'SAMSUNG')">Greet</button>
</form>
</div>
<script>
new Vue({
el: '#app',
methods: {
greet1: function (msg) {
alert('Hello ' + msg + '!');
console.dir(event.target);
},
greet2: function (e, msg) {
if (e) e.preventDefault(); // event의 새로 고침을 막는 역할
alert('Hello ' + msg + '!');
console.dir(e.target);
},
},
});
</script>
</body>
</html>
1번 버튼을 누른 최종 결과는 아래와 같다.
alert 창으로 Hello SAMSUNG을 띄우고 페이지를 이동한다.
2번 버튼을 누른 최종 결과는 아래와 같다.
alert 창으로 Hello SAMSUNG을 띄우고 페이지를 이동하지 않는다.
- Event Modifier
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>페이지 이동</h2>
<a href="test29.html" @click.prevent="sendMsg">페이지 이동 막기</a><br />
</div>
<script>
new Vue({
el: '#app',
methods: {
sendMsg() {
alert('이동할까요?');
},
},
});
</script>
</body>
</html>
그럼 페이지 이동이 되지 않는다.
Vue에선 $refs 속성을 이용해 DOM에 접근할 수 있다. 예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.success {
color: dodgerblue;
}
.fail {
color: darkred;
}
</style>
</head>
<body>
<div id="app">
<h2>엘리먼트 참조하기</h2>
<!-- 아이디 : <input type="text" v-model="id"> -->
아이디 : <input type="text" v-model="id" ref="id" @keyup="idCheck" />
<button @click="idCheck">아이디 중복 체크</button>
<div v-bind:class="{success : isSuccess, fail : isFail}" v-bind:style="myStyle" v-html="msg"></div>
</div>
<script>
new Vue({
el: '#app',
data: {
id: '',
msg: '',
isSuccess: false,
isFail: false,
myStyle: {
fontSize: '25px',
},
},
methods: {
idCheck() {
if (this.id.length < 5 || this.id.length > 12) {
this.msg = `아이디는 5자이상 12자리 이하입니다.`;
this.$refs.id.focus();
console.dir(this.$refs.id);
this.isSuccess = false;
this.isFail = false;
return;
} else {
if (this.id === 'samsung') {
this.msg = `<b>${this.id}</b>는 사용할 수 없습니다.`;
this.isSuccess = false;
this.isFail = true;
} else {
this.msg = `<b>${this.id}</b>는 사용할 수 있습니다.`;
this.isSuccess = true;
this.isFail = false;
}
}
console.log(this.$refs.id.value);
},
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
※ Vue의 가장 중요한 목적 중 하나가 개발자가 DOM을 사용하지 않게 하는 것이므로 $refs가 권장되진 않는다.
v-bind:class는 true, false로 CSS를 지정할 수 있다.
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style type="text/css">
.completed {
text-decoration: line-through;
font-style: italic;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li :class="{completed: todo.done}" :style="myStyle" v-for="todo in todos">
{{todo.msg}}
<button @click="complete(todo)" class="btn">완료</button>
</li>
</ul>
</div>
<script>
new Vue({
el: '#app',
data: {
todos: [
{
msg: '5시간 잠자기',
done: false,
},
{
msg: '알고리즘 1시간 공부하기',
done: false,
},
{
msg: 'Vue 1시간 공부하기',
done: false,
},
],
myStyle: {
fontSize: '20px',
},
},
methods: {
complete: function (todo) {
todo.msg = todo.msg + '완료';
todo.done = !todo.done;
},
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
v-model 디렉티브를 사용하여 Form에 양방향 데이터 바인딩을 생성할 수 있다. 하나씩 살펴보자.
- text, textarea
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
아이디 :
<input v-model.trim="id" placeholder="아이디를 입력하세요" />
<!-- v-model은 기본적으로 모든 key stroke가 발생할 때마다 값을 업데이트 시킨다.
.lazy 수식어를 추가하여 change 이벤트 이후에 동기화 할 수 있습니다. -->
<input v-model.lazy="id" placeholder="아이디를 입력하세요" />
</div>
<div>
메세지 :
<textarea v-model="message" placeholder="내용을 입력하세요"></textarea>
</div>
<p>{{ id }} 님에게 보내는 메세지 : {{ message }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
id: '',
message: '',
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
- checkbox
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>당신이 가고 싶은 지역을 선택하시오</div>
<input type="checkbox" id="buk" value="부울경" v-model="checkedAreas" />
<label for="buk">부울경</label>
<input type="checkbox" id="gwangju" value="광주" v-model="checkedAreas" />
<label for="gwangju">광주</label>
<input type="checkbox" id="gumi" value="구미" v-model="checkedAreas" />
<label for="gumi">구미</label>
<input type="checkbox" id="daejeon" value="대전" v-model="checkedAreas" />
<label for="daejeon">대전</label>
<input type="checkbox" id="seoul" value="서울" v-model="checkedAreas" />
<label for="seoul">서울</label>
<br />
<span>체크한 이름: {{ checkedAreas }}</span>
</div>
<script>
new Vue({
el: '#app',
data: {
checkedAreas: [],
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
- radio
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>수업을 듣는 지역을 선택하시오</div>
<div>
<input type="radio" id="buk" value="부울경" v-model="ckArea" />
<label for="buk">부울경</label>
<input type="radio" id="gwangju" value="광주" v-model="ckArea" />
<label for="gwangju">광주</label>
<input type="radio" id="gumi" value="구미" v-model="ckArea" />
<label for="gumi">구미</label>
<input type="radio" id="daejeon" value="대전" v-model="ckArea" />
<label for="daejeon">대전</label>
<input type="radio" id="seoul" value="서울" v-model="ckArea" />
<label for="seoul">서울</label>
</div>
<span>선택한 지역 : {{ ckArea }}</span>
</div>
<script>
new Vue({
el: '#app',
data: {
ckArea: '광주',
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
- select
단일 선택 가능 실행 예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<p>수업을 듣는 지역을 선택하시오</p>
<select v-model="selectedArea">
<option disabled value="">선택하세요</option>
<option value="buk">부울경</option>
<option value="gwangju">광주</option>
<option value="gumi">구미</option>
<option value="daejeon">대전</option>
<option value="seoul">서울</option>
</select>
</div>
<span>선택한 지역 : {{ selectedArea }}</span><br />
</div>
<script>
new Vue({
el: '#app',
data: {
selectedArea: '',
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
다중 선택 가능 실행 예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<p>수업을 듣고자하는 지역을 선택하시오(다중가능)</p>
<select v-model="selectedArea" multiple>
<option disabled value="">선택하세요</option>
<option value="buk">부울경</option>
<option value="gwangju">광주</option>
<option value="gumi">구미</option>
<option value="daejeon">대전</option>
<option value="seoul">서울</option>
</select>
</div>
<span>선택한 지역 : {{ selectedArea }}</span>
</div>
<script>
new Vue({
el: '#app',
data: {
selectedArea: [],
},
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
HTML을 확장하여 재사용 가능한 코드로 캡슐화한 것이 Component이다.
Component는 전역 Component와 지역 Component로 나뉜다. 예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app1">
<my-global></my-global> // 케밥 케이스 필수!!
<my-global></my-global>
<my-local></my-local> // 케밥 케이스 필수!!
<my-local></my-local>
</div>
<div id="app2">
<my-global></my-global>
<my-global></my-global>
<my-local></my-local>
<my-local></my-local>
</div>
<script>
// 전역 Component
Vue.component("MyGlobal", { // 케밥 케이스 권장
template: "<h2>전역 컴포넌트임</h2>",
});
new Vue({
el: "#app1",
components: {
// 지역 Component
MyLocal: { // 케밥 케이스 권장
template: "<h2>지역 컴포넌트</h2>",
},
},
});
new Vue({
el: "#app2",
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
Component 간 변수는 공유된다. 아래 예시를 살펴보자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<h2>컴포넌트 데이터 공유 문제</h2>
<div id="app">
<count-view></count-view>
<count-view></count-view>
<count-view></count-view>
</div>
<template id="countview">
<div>
<span>{{ count }}</span>
<button @click="count++">클릭</button>
</div>
</template>
<script>
let num = {
count: 0,
};
Vue.component("count-view", {
data() {
return num;
},
template: "#countview",
});
new Vue({
el: "#app",
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
따라서 각 태그마다 데이터를 관리하게 할 수 있다. 아래 예시를 살펴보자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<h2>컴포넌트 데이터 공유 문제 해결</h2>
<div id="app">
<count-view></count-view>
<count-view></count-view>
<count-view></count-view>
</div>
<template id="countview">
<div>
<span>{{ count }}</span>
<button @click="count++">클릭</button>
</div>
</template>
<script>
Vue.component("count-view", {
template: "#countview",
data() {
return {
count: 0,
};
},
});
new Vue({
el: "#app",
});
</script>
</body>
</html>
실행 결과는 아래와 같다.
Vue는 Component 간 데이터 전달을 지원한다.
부모에서 자식은 props라는 특별한 속성, 자식에서 부모는 event로만 데이터 전달이 가능하다.
부모에서 자식으로 데이터를 전달하는 경우부터 살펴보자. 해당 예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h2>props test</h2>
<!-- 동적 props -->
<child-component v-bind:props-data="message"></child-component>
</div>
<script>
//하위 컴포넌트
Vue.component("child-component", {
props: ["propsData"],
template: "<span>{{ propsData }}</span>",
});
new Vue({
el: "#app",
data() {
return {
message: 'hello samsung',
};
},
});
</script>
</body>
</html>
원리는 아래와 같다.
따라서 출력 결과는 아래와 같다.
props로 객체를 전달할 때 객체 이름으로 전체 객체 데이터를 넘길 수 있다. 아래 예시를 살펴보자.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<h2>컴포넌트 객체 데이터 전달</h2>
<div id="app">
<member-view v-bind:member="user"></member-view>
</div>
<template id="memberview">
<div>
<div>이름 : {{ member.name }}</div>
<div>나이 : {{ member.age }}</div>
<div>이메일 : {{ member.email }}</div>
</div>
</template>
<script>
Vue.component("member-view", {
props: ["member"],
template: "#memberview",
});
new Vue({
el: "#app",
data() {
return {
user: {
name: "홍길동",
age: 22,
email: "hong@ssafy.com",
},
};
},
});
</script>
</body>
</html>
출력 결과는 아래와 같다.
다음은 자식에서 부모로 데이터를 전달하는 경우를 살펴보자.
자식은 이벤트를 발생시키기 위해 $emit 메서드를, 부모는 이벤트를 수신하기 위해 $on 메서드를 사용한다.
예시는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h4>당신이 좋아하는 파트를 선택하세요</h4>
<h2>총 투표수 : {{ total }}</h2>
<subject v-on:add-total-count="addTotalCount" title="코딩"></subject>
<subject v-on:add-total-count="addTotalCount" title="알고리즘"></subject>
</div>
<script>
Vue.component("Subject", {
template: '<button v-on:click="addCount">{{title}} - {{ count }}</button>',
props: ["title"],
data: function () {
return {
count: 0,
};
},
methods: {
addCount: function () {
this.count += 1;
// 부모 v-on:이름 에 해당하는 이름의 이벤트를 호출
this.$emit("add-total-count", "인자도 넘길수 있어요."); // payload
},
},
});
new Vue({
el: "#app",
data: {
total: 0,
},
methods: {
addTotalCount: function (msg) {
console.log(msg);
this.total += 1;
},
},
});
</script>
</body>
</html>
출력 결과는 아래와 같다.