뷰 컴포넌트 통신
컴포넌트 간 통신과 유효 범위
vue의 경우 컴포넌트로 화면을 구성하므로 같은 웹 페이지라도 데이터를 공유할 수 없습니다.
그 이유는 컴포넌트마다 자체적으로 고유한 유효 범유 (scope)를 갖기 때문입니다.
이는 뷰 프레임워크 내부적으로 정의된 특징입니다.
따라서 각 컴포넌트의 유효 범위가 독립적이기 때문에 다른 컴포넌트의 값을 직접적으로 참조할 수가 없습니다.
<div id="app">
<my-component1></my-component1>
<my-component2></my-component2>
<div>
<script>
//첫번째 컴포넌트 내용
var cmp1 = {
template: '<div>첫 번째 지역 컴포넌트 : {{ cmp1Data }}<div>',
data: function() {
return {
cmp1Data : 100
}
}
};
//두번째 컴포넌트 내용
var cmp2 = {
template: '<div>두 번째 지역 컴포넌트 : {{ cmp2Data }}<div>',
data: function() {
return {
cmp2Data : cmp1.data.cmp1Data
}
}
};
new Vue({
el: '#app',
// 지역 컴포넌트 등록
components: {
'my-component1': cmp1,
'my-component2': cmp2
}
});
<script>
출력하게 되면 아래와 같은 화면이 나오게 된다.
이렇게 된 이유는 my-component2에서 my-component1의 값을 직접 참조할 수 없기 때문입니다.
이런 이유때문에 상위(부모) - 하위(자식) 컴포넌트간에 데이터 전달 방식이 존재합니다.
상위 - 하위 컴포넌트란 트리 구조에서 부모 노드, 자식 노드처럼 컴포넌트 간의 관계가 부모, 자식으로 이루어진 컴포넌트를 의미합니다.
먼저 상위에서 하위로는 props라는 특별한 속성을 전달합니다.
그리고 하위에서 상위로는 이벤트만 전달할 수 있습니다.
props는 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 때 사용하는 속성이다.
사용하기 위해선
하위 컴포넌트 속성에 아래의 문법으로 정의합니다.
Vue.component('child-component', {
props: ['props 속성 이름'],
});
그런 다음 상위 컴포넌트 템플릿에 아래의 문법을 추가합니다.
<child-component v-bind:props 속성 이름="상위 컴포넌트의 data 속성"></child-component>
v-bind 속성의 왼쪽 값으로 하위 컴포넌트에 정의한 props속성을 넣고, 오른쪽 값으로 하위 컴포넌트에 전달할 상위 컴포넌트의 data 속성을 지정합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
④ <child-component v-bind:propsdata="message"></child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
② Vue.component('child-component', {
③ props: ['propsdata'],
⑤ template: '<p>{{ propsdata }}</p>',
});
① new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
});
</script>
</body>
</html>
① new Vue()로 인스턴스를 하나 생성합니다.
② Vue.component()를 이용하여 하위 컴포넌트인 child-component를 등록합니다.
③ child-component의 내용에 props 속성으로 propsdata를 정의합니다.
④ HTML에 컴포넌트 태그를 추가합니다. < child-component > 태그의 V-bind 속성을 보면, v-bind:propsdata="message"는 상위 컴포넌트의 message 속성 값인 'Hello Vue!' 텍스트를 하위 컴포넌트의 propsdata로 전달하였습니다.
⑤ child-component의 template 속성에 정의 된 '< p >{{ propsdata }}< /p >는 Hello Vue! 가 됩니다.
위의 내용을 정리하면
뷰 인스턴스의 data 속성에 정의 된 message 속성을 하위 컴포넌트에 props로 전달하여 화면에 나타냅니다.
인스턴스에서 새로운 컴포넌트를 등록하면 기존에 있는 컴포넌트는 상위 컴포넌트(부모)가 되고, 새로 등록된 컴포넌트는 하위(자식) 컴포넌트가 됩니다. 그리고 이렇게 새 컴포넌트를 등록 한 인스턴스를 최상위 컴포넌트라고도 부릅니다.
그렇다면 하위에서 상위로 보내는 방법은 무엇일까?
그건 이벤트를 발생 시켜(event emit) 상위 컴포넌트에 신호를 보내면됩니다.
이벤트 발생과 수신은 $emit()과 v-on:속성을 사용하여 구현합니다.
// 이벤트 발생
this.$emit('이벤트 명');
// 이벤트 수신
<child-component v-on:이벤트명="상위 컴포넌트의 메서드명"></child-component>
$emit()을 호출하면 괄호 안에 정의된 이벤트가 발생합니다.
그리고 일반적으로 $emit()을 호출 하는 위치는 하위 컴포넌트의 특정 메서드 내부입니다.
따라서 $emit()을 호출할 때 사용하는 this는 하위 컴포넌트를 가리킵니다.
호출한 이벤트는 하위 컴포넌트를 등록하는 태그(상위 컴포넌트의 template 속성에 위치)에서 v-on:으로 받습니다. 하위 컴포넌트에서 발생한 이벤트명을 v-on: 속성에 지정하고, 속성의 값에 이벤트가 발생했을 때 호출될 상위 컴포넌트의 메서드를 지정합니다.
예제
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
③ <child-component v-on:show-log="printText"></child-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('child-component', {
template: '<button v-on:click="showLog">show</button>',①
② methods: {
showLog: function() {
this.$emit('show-log');
}
}
});
var app = new Vue({
el: '#app',
data: {
message: 'hello'
},
methods: {
④ printText: function() {
console.log("received an event");
}
}
});
</script>
</body>
</html>
이 코드는 child-componentd의 [ show ]버튼을 클릭하여 이벤트를 발생시키고, 발생한 이벤트로 상위 컴포넌트 (여기서 루트 컴포넌트)의 printText()메서드를 실행시키는 예제이다. 과정은 이렇다.
① [show]버튼을 클릭하면 클릭 이벤트 v-on:click="showLog"에 따라 showLog() 메서드가 실행됩니다.
② showLog() 메서드 안에 this.$emit('show-log')가 실행되면서 show-log 이벤트가 발생합니다.
③ show-log 이벤트는 < child-componentd >에 정의한 v-on:show-log에 전달되고, v-on:show-log의 대상 메서드인 최상위 컴포넌트의 메서드 printText()가 실행됩니다.
④ printText()는 received an event라는 로그를 출력하는 메서드 이므로 마지막으로 콘솔에 로그가 출력됩니다.
이와 같은 방식으로 하위 컴포넌트에서 상위 컴포넌트로 신호를 올려보내면 상위 컴포넌트의 메서드를 실행할 수 있습니다.
같은 레벨은 다음에 정리하도록하겠다.