컴포넌트의 데이터는 각 컴포넌트에서 관리하기 때문에 각 컴포넌트는 고유한 데이터 유효범위를 갖는다.
따라서 컴포넌트간 데이터를 주고 받기 위해서는 특별한 규칙을 따라야한다.
위와 같이 상→하로 props를 통해 데이터를 전달하고
하→상은 이벤트를 올려서 전달한다.
*컴포넌트 통신 규칙 : 직접 연관이 되어있는 상, 하위 컴포넌트 간의 데이터 전달이 가능하다. (데이터 오류 발생시 추적에 용이)
1. props (상 -> 하)
props는 위에서 설명한 것과 같이 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방법이다.
부모 컴포넌트에 등록되어 있는 데이터를 가져오는 기본적인 사용 방법은
1. 자식(하위) 컴포넌트에 props 속성을 사용해 프롭스 속성명을 등록해준다.
var appHeader = {
template: '<h1>Title</h1>',
props: ['propsdata'] // 프롭스 속성 명은 임의로 지정이 가능하다.
}
new Vue({
el: '#app',
component: {
'app-header' : appHeader,
},
data: {
message: 'hi',
}
})
2. 자식 컴포넌트에서 v-bind를 사용해서 상위 컴포넌트의 데이터 속성을 해당하는 자식 컴포넌트로 전달한다.
<!-- v-bind:프롭스 명="상위 컴포넌트의 data 속성" -->
<app-header v-bind:propsdata="message"></app-header>
크게 위 1, 2번의 방법으로 적용할 수 있다.
<div id="app">
<app-header v-bind:propsdata="message"></app-header>
<app-content v-bind:propsnum="num"></app-content>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var appHeader = {
template: '<h1>{{ propsdata }}</h1>', // {{ 데이터 }} 이중 괄호를 통해 부모 컴포넌트로부터 받은 props 값을 그대로 뿌려줄 수 있다.
props: ['propsdata'],
}
var appContent = {
// 각 영역 안에 데이터는 고유의 데이터로 가진다. 따라서 appHeader와 마찬가지로 propsdata라는 같은 이름으로 사용해도 된다.
template: '<div>{{ propsnum }}</div>',
props: ['propsnum'],
}
new Vue({
el: '#app',
components: {
'app-header' : appHeader,
'app-content' : appContent,
},
data: {
message: 'hi',
num: 10
}
});
</script>
2. Event Emit (하 -> 상)
부모 컴포넌트의 데이터를 가져올 때 props 속성을 사용한다면
자식 컴포넌트에서는 이벤트 발생(emit)을 통해 상위로 이벤트를 전달해준다.
방법은
1. 자식 컴포넌트에 event emit 메소드를 등록해준다.
// appHeader에 있는 button을 클릭하면 passEvent 함수가 실행되고 그 함수는 'pass'라는 이벤트명을 가진 event emit용 함수이다.
var appHeader = {
template: '<button v-on:click="passEvent">click me</button>',
methods: {
passEvent: function(){ //passEvent라는 메소드를 통해 해당 자식 컴포넌트에 $emit 이벤트명은 정의한다.
this.$emit('pass'); // 이벤트 이름 등록 : $emit('이벤트명')
}
}
}
new Vue({
el: '#app',
components: {
'app-header': appHeader,
},
methods: {
logText: function(){
console.log('hi');
},
}
})
2. 자식 컴포넌트에 $emit으로 등록한 이벤트 명을 통해 부모 컴포넌트의 메소드를 가져온다.
<!-- v:on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메소드 이름" -->
<app-header v-on:pass="logText"></app-header>
위와 같이 작업하면 app-header 영역에 버튼이 생기고
해당 버튼을 클릭하면 자식 요소의 버튼을 클릭하지만 부모 요소에 logText() 메소드가 실행된다.
일반적인 웹에서는 html, css, javascript를 이용해서 화면을 구성하고 조작한다.
Vue.js
<div id="app"></div>
<script>
const app = document.querySelector('#app');
let str = 'hello';
app.innerHTML = str; // #app 영역에 hello 텍스트 노출
str = 'Hi';
app.innerHTML = str; // str 변수의 값을 바꿨지만 바뀐 내용이 즉시 반영되는 것이 아닌 innerHTML 을 통해 다시 선언해줘야 바뀐 변수값이 #app 영역에 적용된다.
</script>
반면 Vue.js는 MVVM(Model-View-ViewModel) 패턴으로
화면에서 보여지는 내용들을(View) Vue의 Dom Listeners를 통해 자바스크립트에 있는 데이터를 바꿔주거나 특정 자바스크립트를 실행하게 된다.
그리고 Dom Listeners를 통해 자바스크립트의 데이터가 변경 됐을 때 Data Bindings를 통해 변경된 내용을 화면에 반영하게 된다.
<div id="app"></div>
<script>
const app = document.querySelector('#app');
const viewModel = {};
// Object.defineProperty(대상 객체, 객체의 속성, {
// 정의할 내용
// })
Object.defineProperty(viewModel, 'str', {
// 속성에 접근했을 때의 동작을 정의
get: function(){
console.log('접근') // 콘솔창에서 viewModel.str 을 입력하면 '접근' 출력
}
// 속성에 값을 할당했을 때의 동작을 정의
set: function(newValue){
console.log('할당', newValue) // 콘솔창에서 viewModel.str = 'Hi' 를 입력하면 '할당 10' 출력
app.innerHTML = newValue // viewModel.str 값을 할당/변경할 때 마다 해당하는 값을 app에 뿌려준다.
}
});
</script>
* Object.defineProperty : 객체의 속성에 접근, 할당 했을 때의 동작을 정의한다.
위와 같이 데이터를 접근/변경할 때 마다 바로바로 데이터를 바인딩 시켜주는 것은 Vue.js의 가장 중요한 특징인 Reactivity이다.