반응형
v-model을 이용하여 단순 상태 업데이트 관련 코드 상용구를 줄여봅니다.
v-model의 사용사례
프런트엔드에서 양식을 처리할 때 양식 입력 요소의 상태를 JavaScript의 해당 상태와 동기화해야 하는 경우가 많습니다.
값을 수동으로 바인딩하고 이벤트 리스너를 연결하는 것은 번거로울 수 있습니다.
Vue3 공식문서
v-model은 form input값과 같은 컴포넌트 내부 상태(값)와 외부 반응형 값의 상태 동기화를 쉽게 만들어 줍니다.
v-model의 비밀
이전 게시물에서 언급했듯이, 실제론 단방향 바인딩으로 컴파일 되어 처리합니다.
<Comp
:modelValue="first"
@update:modelValue="newValue => first = newValue"
/>
만약 v-model이 아니었다면 항상 저런 패턴으로 작성해야 합니다.
v-model 쉽게 구현하기
컴파일러가 디폴트로 적용하는 modelValue를 넘어
한 컴포넌트 내에 여러개의 v-model 바인딩을 프롭으로 허용하고 싶다면 어떻게 할까요?
라는 기능을 활용하면 됩니다.
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
위와 같이 인수를 사용해 여러개의 v-model을 사용할 수 있습니다.
위 컴포넌트는 실제 아래와 같이 처리될 것입니다.
<UserName
:first-name="first"
:last-name="last"
@update:first-name="newValue => first = value"
@update:last-name="newValue => last = value"
/>
여러개의 v-model을 활용하는 컴포넌트
직접 개발해 봅시다!
1. 간단한 컴포저블 정의
프롭을 이용하는 계산된 값을 만들고, 적절한 이벤트를 방출(emit)하여
프롭의 변경을 컴포넌트에 반영할 수 있게 해줍니다.
import {computed} from "vue"
export function useModelWrapper<T extends Record<string, unknown>, R extends string>(
props: T,
emit: (e: `update:${R}`, ...args: any[]) => void,
name: R
) {
return computed({
get: () => props[name],
set: value => emit(`update:${name}`, value)
})
}
2. 자식 컴포넌트 정의
- default v-model로 바인딩된 프롭은 modelValue로 전달됩니다.
- emit('update:modelValue',새값)을 방출하여 부모 컴포넌트의 props을 변경할 수 있도록 합니다.
- 부모에서 v-model:draft로 전달된 프롭은 draft로 전달됩니다.
- emit('update:draft',새값)을 방출하여 부모 컴포넌트의 props을 변경할 수 있도록 합니다.
<template>
<label>
<input type="text" v-model="message" />
<input type="checkbox" v-model="isDraft" /> Draft
</label>
</template>
<script setup lang="ts">
import { defineEmits, defineProps } from "vue";
import { useModelWrapper } from "../useModelWrapper";
const emit = defineEmits(["update:modelValue", "update:isDraft"]);
const props = defineProps<{ modelValue: string; draft: boolean }>();
const message = useModelWrapper(props, emit, "modelValue");
const isDraft = useModelWrapper(props, emit, "draft");
</script>
3. 부모 컴포넌트에서 사용
2에서 정의한 대로 v-model, v-model:draft를 활용합니다.
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<h2>model : {{ model }}</h2>
<h2>draft : {{ draft }}</h2>
<MyForms v-model="model" v-model:draft="draft" />
</template>
<script setup lang="ts">
import { ref } from "vue";
import MyForms from "./components/MyForms.vue";
const model = ref("test");
const draft = ref(false);
</script>
기타 : input v-model 한글 지원
아시다시피 html input에 그대로 v-model을 사용하면 한자, 한글이 제대로 완성이 안되는 문제가 있습니다.
이는 아래와 같이 wrapper 컴포넌트를 하나 만드는 방법으로 우회 가능합니다.
참고
Vue3 공식문서 : Form Input Bindings
반응형
'FrontEnd' 카테고리의 다른 글
[번역] JS 번들 분할의 모든 것 (0) | 2022.12.26 |
---|---|
Vue3 컴포넌트 디자인 패턴 : expose (0) | 2022.12.25 |
Vue3 컴포넌트 디자인 패턴 : Object binding (0) | 2022.12.22 |
Vue3 컴포넌트 디자인 패턴 : Scoped Slots (0) | 2022.12.22 |
Vue3 컴포넌트 디자인 패턴 : Slots (0) | 2022.12.22 |