이전에 정리한 글인 props와 emit에 정리하면서 v-model를 활용할 수 있다는 글을 보고 정리하고자 한다.
부모 컴포넌트와 자식 컴포넌트의 데이터 바인딩
코드의 재사용을 위해 컴포넌트를 분리하면, 부모 컴포넌트와 자식 컴포넌트의 데이터 바인딩이 필요하다.
- 부모 컴포넌트의 값을 자식 컴포넌트에게 내려준다.
- 자식 컴포넌트에서 값이 변경되면, 그 사실을 부모 컴포넌트에도 알려야한다.
v-model
부모 컴포넌트
<!-- App.vue -->
<template>
<div>
<div>parent-compo {{number}}</div>
<child-compo v-model="number" />
</div>
</template>
<script>
import ChildCompo from "@/components/ChildCompo";
export default {
data () {
return {
number: 0
}
},
components: {
ChildCompo
}
}
</script>
부모 컴포넌트에서 v-model로 number라는 값을 내려주면, 자식 컴포넌트에서는 props로 데이터를 접근할 수 있다.
자식 컴포넌트
<!-- ChildCompo.vue -->
<template>
<div>
<div>child-compo {{value}}</div>
<button @click="value += 1">Add 1</button>
</div>
</template>
<script>
export default {
props: {
value: Number
}
}
</script>
<script>
export default {
props: {
value: Number
}
}
</script>
💫 단, v-model로 바인딩 한 값은props에서 value로만 선언해야 한다.
v-model을 사용하면 발생하는 문제
v-model을 사용하면 자식 컴포넌트에서 값을 바꾸어도 부모 컴포넌트에는 적용되지 않는 이슈가 발생한다.
props는 readonly인데, 자식 컴포넌트에서 값을 직접 바꿔서 발생하는 오류이다. [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"
props는 이전에도 정리했듯이 단방향 데이터 방식을 사용한다. 그러나 지금 양방향 바인딩 방식을 사용할려고 하니 나는 오류이다. 또한, props는 불변하다.
:value, @input
💫 v-model은 :value와 @input의 축약 문법이다.
여기서 @input는 input태그에 입력한 값을 말한다.
부모 컴포넌트
<!-- App.vue -->
<template>
<div>
<div>parent-compo {{number}}</div>
<child-compo :value="number" @input="onChangeNumber" />
</div>
</template>
<script>
import ChildCompo from "@/components/ChildCompo";
export default {
data () {
return {
number: 0
}
},
methods: {
onChangeNumber (val) {
this.number = val;
}
},
components: {
ChildCompo
}
}
</script>
- 하위 컴포넌트 가져오기 : 하위 컴포넌트에 data를 전달하기 이전에, 하위 컴포넌트를 임포트해서 가져온다.
- data 선언 : 하위 컴포넌트에 주고자 하는 data를 선언한다.
- 하위 컴포넌트에 전달 : v-on디렉티브를 사용하여 data명과 일치하게 변수값도 선언한다. 헷갈리기에 이렇게 선언하는 것이 좋다. (:변수명 = data)
- 함수 선언 : 하위 컴포넌트에서 데이터값이 변화된 것을 상위 컴포넌트에 실행하기 위해 함수를 선언해준다.
자식 컴포넌트
<!-- ChildCompo.vue -->
<template>
<div>
<div>child-compo {{value}}</div>
<button @click="onClickButton">Add 1</button>
</div>
</template>
<script>
export default {
props: {
value: Number
},
data () {
return {
pvalue: this.value
}
},
methods: {
onClickButton() {
this.$emit('input', this.pvalue++) // 부모에게 값이 변경되었다는 것을 알림
}
}
}
</script>
- props로 변수 가져오기 : props로 선언한 값 가져온다.
- 함수 선언 : 상위 컴포넌트에서 변한 변수값을 알기 위해서 함수를 선언하였다. 하위 컴포넌트에서도 div태그를 클릭시 함수를 선언한다. 그리고 여기 수정된 값을 change emit를 통해 상위 컴포넌트로 넘겨준다.
- 상위 컴포넌트 업데이트 : count 데이터를 상위 컴포넌트로 넘겨주어 데이터를 업데이트한다.
예시
부모 컴포넌트
<!-- App.vue -->
<template>
<div>
<div>{{title}}</div>
<child-compo @updateTitle="onChangeTitle" />
</div>
</template>
<script>
import ChildCompo from "@/components/ChildCompo";
export default {
data () {
return {
title: '클릭 전'
}
},
methods: {
onChangeTitle () {
this.title = '클릭했다'
}
},
components: {
ChildCompo
}
}
</script>
자식 컴포넌트
<!-- ChildCompo.vue -->
<template>
<div>
<button @click="onClickButton">Change Title</button>
</div>
</template>
<script>
export default {
methods: {
onClickButton() {
this.$emit('updateTitle')
}
}
}
</script>
예를 들어 자식 컴포넌트에서 어떤 버튼을 클릭했을 때, 부모 컴포넌트의 글자를 변경해주는 경우와 같이 자식 컴포넌트에서 굳이 값을 내려주지 않아도 되는 경우가 있다. 그러면 @메서드이름만 사용해도 된다.
🔗 참고
https://velog.io/@okyungjin/Vue-부모-컴포넌트와-자식-컴포넌트의-데이터-바인딩-v-model-value-input