Component
- Vue의 가장 강력한 기능 중 하나
- HTML Element를 확장하여 재사용 가능한 코드를 캡슐화
- Vue Component는 Vue Instance이기도 하기 때문에 모든 옵션 객체를 사용
- Life Cycle Hook 사용 가능
중복 코드를 제거하고 유지보수가 쉬운 코드를 작성할 수 있으며, 애플리케이션 규모가 큰 경우에도 쉽게 확장 가능
전역 component
- Vue.component(tagName, options)로 등록
- 권장하는 컴포넌트 이름 : 케밥 표기법 (전부 소문자, -)
<!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>
</div>
<div id="app2">
<my-global></my-global>
<my-global></my-global>
</div>
<script>
Vue.component("MyGlobal", {
template: "<h2>전역 컴포넌트임</h2>",
});
new Vue({
el: "#app1",
});
new Vue({
el: "#app2",
});
</script>
</body>
</html>
지역 component
- 컴포넌트를 components 인스턴스 옵션으로 등록
- 특정 인스턴스 내에서만 사용 가능한 컴포넌트
<!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-local></my-local>
<my-local></my-local>
</div>
<div id="app2">
<my-local></my-local>
<my-local></my-local>
</div>
<script>
new Vue({
el: "#app1",
components: {
MyLocal: {
template: "<h2>지역 컴포넌트</h2>",
},
},
});
new Vue({
el: "#app2",
});
</script>
</body>
</html>
component template
- Vue.js에서 컴포넌트를 정의할 때, data 옵션에 객체를 직접 할당하는 것이 아니라 함수를 사용해야 함
- 컴포넌트가 재사용될 때 데이터가 공유되는 것을 방지하기 위해!!
<!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">
<my-comp></my-comp>
</div>
<template id="mycomp">
<div>
<h2>{{msg}}</h2>
</div>
</template>
<script>
// Vue.component('MyComp', {
// template: '#mycomp',
// data: {
// msg: 'hello component',
// },
// });
Vue.component("MyComp", {
template: "#mycomp",
data() {
return {
msg: "hello component",
};
},
});
new Vue({
el: "#app",
});
</script>
</body>
</html>
Component 간 통신
- 상위(부모) - 하위(자식) 컴포넌트 간의 data 전달 방법
- 부모에서 자식 : props라는 특별한 속성을 전달 (Pass Props)
- 자식에서 부모 : event로만 전달 가능 (Emit Event)
상위에서 하위 컴포넌트로 data 전달, 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>
<div id="app">
<h2>props test</h2>
<child-component props-data="안녕하세요"></child-component>
</div>
<script>
//하위 컴포넌트
Vue.component("child-component", {
props: ["propsData"],
template: "<span>{{ propsData }}</span>",
});
new Vue({
el: "#app",
// data() {
// return {
// message: 'hello',
// };
// },
});
</script>
</body>
</html>
Vue.js에서는 props의 이름을 camelCase로 정의하지만,
HTML에서는 소문자와 대문자를 구분하지 않기 때문에
kebab-case(하이픈)로 작성된 속성 이름을 props 이름으로 사용해야함
동적 props
- v-bind 를 사용하여 부모의 데이터에 props를 동적으로 바인딩 할 수 있음
- 데이터가 상위에서 업데이트 될 때마다 하위 데이터로도 전달됨
- v-bind 에 대한 단축 구문을 사용하는 것이 더 간단함
<!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">
<child-comp
v-for="(area, i) in areas"
:key="i"
:area="area"
v-bind:msg="msg[parseInt(Math.random() * 4)]"
>
</child-comp>
</div>
<template id="childcomp">
<div>
<h2> {{area}}이(가) 여행지로 {{msg}}</h2>
</div>
</template>
<script>
Vue.component("childComp", {
props: {
area: String,
msg: {
type: String,
require: true,
},
},
template: "#childcomp",
});
new Vue({
el: "#app",
data: {
areas: ["서울", "부산", "제주"],
msg: ["굿^^", "최고!!", "좋아!!", "짱!!"],
},
});
</script>
</body>
</html>
오브젝트의 모든 속성을 전달 할 경우, v-bind:prop-name 대신 v-bind만 작성함으로써 모든 속성을 prop으로 전달 할 수 있음
<!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 :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@example.com",
},
};
},
});
</script>
</body>
</html>
하위에서 상위 컴포넌트로 event 전달
- 하위 컴포넌트에서 상위 컴포넌트가 지정한 이벤트를 발생 $emit
- 상위 컴포넌트는 하위 컴포넌트가 발생한 이벤트를 수신(on) 하여 data 처리
하위 컴포넌트 (자식 컴포넌트)
<template>
<button v-on:click="sendMessage">메시지 보내기</button>
</template>
<script>
export default {
methods: {
sendMessage() {
this.$emit('message-sent', '안녕하세요!');
}
}
};
</script>
상위 컴포넌트 (부모 컴포넌트)
<template>
<div>
<child-component v-on:message-sent="handleMessage"></child-component>
<p>{{ message }}</p>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
message: ''
};
},
methods: {
handleMessage(message) {
this.message = message;
}
}
};
</script>
<!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>
비 상하위간 통신
- 비어 있는 Vue Instance 객체를 Event Bus로 사용
- 복잡해질 경우 상태관리 라이브러리인 Vuex 사용 권장
<!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">
<my-count></my-count>
<log></log>
</div>
<template id="mycount">
<div>
<input type="number" v-model.number="count" @keyup.enter="send" />
<button @click="send">보내기</button>
</div>
</template>
<template id="log">
<div>
<h2>{{count}}</h2>
<ul>
<li v-for="msg, index in list" :key="index">{{msg}}</li>
</ul>
</div>
</template>
<script>
const bus = new Vue();
Vue.component("my-count", {
template: "#mycount",
data() {
return {
count: 0,
};
},
methods: {
send() {
bus.$emit("updateLog", this.count);
this.count = "";
},
},
});
Vue.component("log", {
template: "#log",
data() {
return {
count: 0,
list: [],
};
},
methods: {
updateLog(data) {
this.count += data;
this.list.push(`${data}을 받았습니다.`);
},
},
created: function () {
bus.$on("updateLog", this.updateLog);
},
});
new Vue({
el: "#app",
});
</script>
</body>
</html>
'WEB > front-end' 카테고리의 다른 글
[Vue 기초 6] router (0) | 2023.05.14 |
---|---|
[Vue 기초 5] vue-cli/npm (1) | 2023.05.14 |
[Vue 기초 3] Event, Binding (0) | 2023.05.05 |
[Vue 기초 2] Vue Instance 속성 (method, filter, computed, watch) (0) | 2023.05.04 |
[Vue 기초 1] Vue instance, directive (0) | 2023.05.04 |