[Vue] Vue3+typescript 공통 컴포넌트 만드는 방법
보통 우리가 부모컴포넌트에서 자식컴포넌트로 데이터를 넘겨주고, 변경하기 위해선 props 와 emit을 주로 사용한다.
공통 컴포넌트를 만들면서 props로 전달받은 값을 직접 변경할 수 없기 때문에 Object로 통째로 넘겨주거나(v-model을 사용하기 위해) 혹은 emit을 이용해서 결국 부모컴포넌트에서 emit을 이용해 받아온 값으로 수정을 해주었다.
쓰면서 너무 불편했고 공통 컴포넌트가 맞나 생각이 들던 와중에 새로운 방법을 발견했다.
container.vue (부모컴포넌트임)
<template>
<List
v-model:time="dummy.time"
v-model:duration="dummy.duration"
/>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
interface containerType {
time : string;
duration: number;
}
export default defineComponent({
name: 'container',
setup() {
const dummy = ref<containerType>({time: 'DAY', duration:1});
return {dummy}
}
})
</script>
List라는 공통 컴포넌트를 만들고 여러군데에서 사용한다고 가정하자.
보통 우리가 props를 넘겨줄땐 :time="dummy.time" 을 사용한다.
허나 v-model:time="dummy.time"으로 넘겨준 것을 확인할 수 있다.
이렇게 v-model:{props이름} 을 통째로 넘겨주면 vue 내부에서 emit을 만든다고 한다.
그러면 공통컴포넌트인 List에선 어떻게 사용해주면 될까?
List.vue (공통 컴포넌트)
<template>
<div class="form-group mr-1 ml-1">
<label for="timeLabel">시간 단위</label>
<select :value="time" @change="changeData($event, 'time')" class="custom-select" id="timeLabel" style='width: 100px'>
<option v-for="(day, index) in repeatTimeList" :key="index">
{{day}}
</option>
</select>
</div>
<div class="form-group mr-3">
<label for="durationLabel">기간</label>
<input :value="duration" @change="changeData($event, 'duration')" type="number" class="form-control form-control-sm repeat_input_form" id="durationLabel" placeholder="초기화 전체 기간을 입력하세요">
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, toRefs } from 'vue'
import { Time } from '@/models/store'
export default defineComponent({
name: 'List',
props: {
time: String as PropType<Time>,
duration: Number as PropType<number>,
},
emits: ['update:time', 'update:duration'],
setup(props, { emit }) {
const repeatTimeList = ref<Time[]>(['DAY', 'MONTH']);
function changeData(event: Event, target: string) {
switch(target) {
case 'time':
emit('update:time', (event.target as HTMLSelectElement).value);
break;
case 'duration':
emit('update:duration', (event.target as HTMLSelectElement).value);
break;
}
}
return { repeatTimeList, setRepeatData }
}
})
</script>
<style lang="scss" scoped>
.repeat_input_form {
min-height: 35px;
}
</style>
보면 emits로 ['update:{props이름}] 설정해주고, 전달받은 props들을 :value로 보여준다.
변경같은경우에는 @change 이벤트를 이용하여 emit을 불러와주면 된다.
이러면 부모컴포넌트에서 emit과 관련된 부분을 전혀 신경쓸 필요없이 List(공통컴포넌트)에서 데이터의 변경을 관리할 수 있다.
드디어 진정한 공통 컴포넌트의 의미를 가지게 된것 같다.
기회가되면 저렇게 사용하지 않고 기존 방식대로 부모컴포넌트에서 emit을 썼을 경우를 보여주며 비교 하겠음.
혹시 'update:{props이름}' 인데 props를 object로 넘길수도 있는지 아는사람 알려주면 감사하겠다.. props로 object덩어리만 넘기게.. 너무많아서 ..
(부족한 부분 있으면 댓글 남겨주시면 감사하겠습니다.)