부모 -> 자식으로의 데이터 흐름 : pass props 방식 (props 데이터)
자식 -> 부모로의 데이터 흐름 : emit event 방식 (이벤트 발생)
Pass Props
부모 -> 자식으로의 data 전달 방식
정적인 데이터를 전달 하는 경우에는 static props라고 명시하기도 한다.
요소의 속성을 사용하여 데이터 전달
자식의 props는 부모(상위) 컴포넌트의 정보를 전달하기 위한 사용자 지정 특성이다.
자식(하위) 컴포넌트는 props 옵션을 사용하여 수신하는 props를 명시적으로 선언해야 한다.
prop-data-name="value" 형태로 데이터를 전달한다. 속성 키 값은 kebab-case를 사용해야 한다. HTML은 대소문자를 구분하지 않기 때문이다. HTML 속성으로 보내고 javascript로 받기 때문에 보낼때는 kebab-case이고 받을때의 js는 camel-case를 사용해야 한다.
msg-title="내용" 으로 내려 보내면,
msgTitle : String 으로 받아야 한다.
<template>
<div id="app">
<HelloWorld msg-title="Welcome to Your Vue.js App"/>
</div>
</template>
App.vue
<script>
export default {
name: 'HelloWorld',
props: {
msgTitle: String
}
}
</script>
HelloWorld.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<MyComponent/>
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
App.vue
부모인 App.vue에는 요소의 속성으로 msg 데이터를 전달하고있다. msg라고하는 임의의 속성(property)을 지정해서 값을 내려준것이다.
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
HelloWorld.vue
script의 props에 msg가 있다. props는 msg 데이터가 부모(App.vue)로부터 온 것을 선언한 것이다.
App.vue에서 property로 넘긴 msg를 사용하도록 선언해야 한다.
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
HelloWorld.vue의 자신에게 부여된 msg property를 template에 {{msg}} 형태로 사용한 것이다.
부모쪽의 msg 속성의 값을 수정하면 받는 부분에서 바로 수정이 반영된다.
MyComponent에서 MyItem으로 데이터를 내려주려고 한다.
<template>
<div class="border">
<h1> 새로운 컴포넌트 </h1>
<MyItem static-props="이것은 MyComponent에서 보낸 데이터"/>
<MyItem/>
<MyItem/>
</div>
</template>
MyComponent.vue
데이터를 주고싶은 컴포넌트의 요소로 내려주었다.
요소를 보내주지 않은 MyItem 컴포넌트도 같이 비교한다.
<template>
<div>
<h3>MyComponent의 하위 컴포넌트</h3>
{{staticProps}}
</div>
</template>
<script>
export default {
name:'MyItem',
props:{'staticProps' : String
}
}
</script>
MyItem.vue
props에 요소이름과 타입을 선언하고, template에서 사용했다.
static-props라는 케밥케이스로 내려준 변수를 카멜케이스 staticProps로 자동으로 변환하여 인식하기 때문에 카멜케이스로 선언한다.
속성값으로 보내준 컴포넌트에 데이터가 전달되어 적용되었다.
Dynamic props
변수를 props로 전달할 수 있다.
v-bind directive를 사용해서 데이터를 동적으로 바인딩한다.
속성="값"으로 보내줄때 값이 고정된 문자열이 아니라 js 표현식이 되야 한다. 이를 위해서 v-bind를 사용한다.
부모 컴포넌트 데이터가 업데이트되면 자식 컴포넌트로 전달되는 데이터 또한 업데이트 된다.
<template>
<div class="border">
<h1> 새로운 컴포넌트 </h1>
<MyItem static-props="이것은 MyComponent에서 보낸 데이터"/>
<MyItem :dynamic-props="dynamicProps"/>
<MyItem/>
</div>
</template>
<script>
import MyItem from '@/components/MyItem.vue'
export default {
name : 'MyComponent',
data : function(){
return{
dynamicProps : '이것은 동적 데이터!'
}
},
components:{
MyItem,
}
}
</script>
MyComponent.vue
:요소로 bind를 사용했다. dynamic-props이라는 케밥케이스로 요소이름을 지정했다.
value로는 문자열로 data 이름을 지정해주었고 data는 function의 return에서 작성했다.
data : function(){
return {
데이터
}
}
각 vue 인스턴스는 data 객체를 공유하기 때문에 새로운 data객체를 반환(return)해서 사용해야 한다. 이렇게 해주지않으면 값을 독립적으로 사용할 수 없다.
https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function
Components Basics — Vue.js
Vue.js - The Progressive JavaScript Framework
v2.vuejs.org
<template>
<div>
<h3>MyComponent의 하위 컴포넌트</h3>
{{staticProps}}
{{dynamicProps}}
</div>
</template>
<script>
export default {
name:'MyItem',
props:{
staticProps : String,
dynamicProps : String,
}
}
</script>
MyItem.vue
props에 dynamicProps를 선언하고 template에서 사용했다.
<MyItem :dynamic-props="dynamicProps"/>
props에서 선언한 dynamicProps는 여기서 :dynamic-props를 의미한다. "" 안의 값은 data일 뿐이다.
v-bind로 묶여있는 ""안의 구문은 javascript의 구문으로 볼 수 있고, 변수에 대한 data를 전달하는 것이다.
숫자 1을 props로 전달하기 위해서는 다음 두 방법중 어떤 게 맞을까?
첫번째는 문자열 "1"을 보내는 것이고
두번째는 자바스크립트 "1" 으로 숫자 1이다. 두번째 방법이 맞다.
단방향 데이터 흐름
모든 props는 부모에서 자식으로, 즉 아래로 단방향 바인딩을 형성한다.
부모속성이 업데이트되면 자식으로 흐르지만 반대방향은 아니다. (부모 컴포넌트가 업데이트되면 자식 컴포넌트의 모든 prop들이 최신값으로 업데이트된다.)
하위 컴포넌트가 실수로 상위컴포넌트 상태를 변경하여 앱의 데이터흐름을 이해하기 힘들게 만드는 것을 방지하는 것이다.
하위 컴포넌트에서 prop을 변경하려고 시도하면 경고가 출력된다.
Emit Event
자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달할 때는 이벤트를 발생시킨다.
데이터를 이벤트리스너의 콜백함수의 인자로 전달하고 상위컴포넌트는 해당 이벤트를통해 데이터를 받는다.
$emit
$emit메서드를 통해 부모 컴포넌트에 이벤트를 발생
$emit('evnet-name') 형식으로 부몬컴포넌트에 event-name 이라는 이벤트가 발생했다는 것을 알린다.
$
javascript는 변수에 _, $ 두개의 특수문자를 사용 가능하다.
이때 기존에 사용하던 변수, 메서드와 겹치지 않게 하기위해 vue는 $emit을 이벤트 전달
<template>
<div>
<h3>MyComponent의 하위 컴포넌트</h3>
{{staticProps}}
{{dynamicProps}}
<button @click="childToParent">클릭</button>
</div>
</template>
<script>
export default {
name:'MyItem',
props:{
staticProps : String,
dynamicProps : String,
},
methods:{
childToParent : function(){
this.$emit('child-to-parent')
}
}
}
</script>
MyItem.vue
하위 컴포넌트인 MyItem.vue에서 버튼이 클릭이벤트가 발생하면 부모에게 소리칠 메서드를 실행한다.
소리치는 메서드는 emit을 사용한다. emit은 객체의 기본 속성이기때문에 $가 붙는다.
최상위 컴포넌트로 점프할 수는 없고, 바로 상위의 컴포넌트에 emit 된다.
child-to-parent는 emit이벤트의 이름일 뿐이다.
<template>
<div class="border">
<h1> 새로운 컴포넌트 </h1>
<MyItem @child-to-parent="parentGetEvent" :dynamic-props="dynamicProps" static-props="이것은 MyComponent에서 보낸 데이터"/>
</div>
</template>
<script>
import MyItem from '@/components/MyItem.vue'
export default {
name : 'MyComponent',
data : function(){
return{
dynamicProps : '이것은 동적 데이터!'
}
},
components:{
MyItem,
},
methods:{
parentGetEvent:function(){
console.log('emit 들었음')
}
}
}
</script>
MyComponent.vue
자식이 보내준 child-to-parent라는 emit 이벤트가 발생하면(v-on:child-to-parent) parentGetEvent라는 메서드를 실행하도록 했다.
자식 컴포넌트의 버튼을 클릭하면, 부모의 메서드가 실행되어 console에 출력된다.
methods:{
childToParent : function(){
this.$emit('child-to-parent','자식이 보낸 데이터')
}
}
MyItem.vue
emit의 두번째 인자로 데이터를 보내줄 수있다.
methods:{
parentGetEvent:function(childData){
console.log('emit 들었음')
console.log(childData)
}
}
MyComponent.vue
자식이 보내준 데이터 인자를 매개변수로 받아서 이용할 수있다.
버튼을 클릭하면 이와같이 자식에서 보내준 데이터를 출력할 수 있다.
간접적으로 이벤트의 인자로 데이터를 보내주었다.
Emit Event 흐름 정리
1. 자식 컴포넌트에 있는 버튼 클릭 이벤트를 청취하여 연결된 핸들러함수(childToParent) 호출
2. 호출된 함수에서 $emit을 통해 상위 컴포넌트에 이벤트(child-to-parent) 발생
3. 상위 컴포넌트는 자식 컴포넌트가 발생시킨 이벤트(child-to-parent)를 청취하여 연결된 핸들러함수(parentGetEvent)호출
이렇게 전달한 데이터는 이벤트와 연결된 부모 컴포넌트의 핸들러 함수 인자로 사용 가능하다.
<input
type="text"
v-model="childInputData"
@keyup.enter="childInput">
methods:{
childToParent : function(){
this.$emit('child-to-parent','자식이 보낸 데이터')
},
childInput:function(){
this.$emit('child-input',this.childInputData)
}
}
MyItem.vue
v-model로 양방향바인딩하여 input 태그에서 사용자가 입력하는 값을 childInputData라는 변수에 데이터로 저장한다.
keyup.enter 동작때 실행할 메서드를 만들어준다.
메서드는 emit 이벤트이름으로 child-input을 설정하고 childInputData라는 데이터를 인자로 보내준다.
<MyItem
@child-input="getDynamicData"
@child-to-parent="parentGetEvent"
:dynamic-props="dynamicProps"
static-props="이것은 MyComponent에서 보낸 데이터"/>
MyComponent.vue
상위 컴포넌트에 하위컴포넌트 속성으로 @child-input 이벤트가 작동할때 메서드를 실행하도록 한다.
methods:{
parentGetEvent:function(childData){
console.log('emit 들었음')
console.log(childData)
},
getDynamicData : function(input){
console.log(`사용자가 입력한 값은 ${input}입니다.`)
}
}
메서드는 getDynamicData라는 이름이고 인자로 받아온 데이터를 이용한다.
자식컴포넌트의 input 태그에 값을 입력하고 엔터치면 메서드가 실행되어 emit으로 이벤트를 발생시키고,
부모가 데이터를 이용한 메서드를 실행한다.
childInput:function(){
this.$emit('child-input',this.childInputData)
this.childInputData=""
}
입력받으면 데이터를 보내준 뒤에, input 입력값을 비워주기 위해서는 변수 값을 ""로 비워주는 동작을 추가한다.
kebab-case / camelCase
HTML 요소에서 사용할 때 : kebab-case
JavaScript에서 사용할 때 : camelCase
props
상위 -> 하위 흐름에서 HTML 요소로 내려줌 : kebab-case
하위에서 받을 때 JavaScript에서 받음 : camelCase
emit
emit 이벤트를 발생시키면 HTML 요소가 이벤트를 청취 함 : kebab-case
메서드, 변수면 등은 Javascript에서 사용함 : camelCase
'Front-end > Vue.js' 카테고리의 다른 글
Vuex state management - store : state, actions, mutations, getters (0) | 2022.11.07 |
---|---|
vue - youtube api (0) | 2022.11.03 |
Vue CLI / Module / Vue 프로젝트, 컴포넌트 생성 (0) | 2022.11.02 |
computed, method 비교 / watch / filters (0) | 2022.10.31 |
Template Syntax - text interpolation, directives(v-text, v-html, v-show, v-if, v-bind, v-on, v-model) (0) | 2022.10.31 |