[Vue.js] 새로운 v-model 살펴보기#5(Multi root components)

하수도키

·

2021. 3. 1. 14:55

728x90
반응형
SMALL

이번 포스팅에서는 Multi-Root Components(멀티 루트 컴포넌트)를 살펴 보겠다.

Vue2에서는 멀티 루트 컴포넌트랑 fragments를 사용할 수 없었고 사용하려면 라이브러리 같은 것을 불러와야 가능했다.

Vue3에서는 라이브러리 사용 없이 멀티 루트 컴포넌트랑 fragments를 사용할 수 있다.

싱글 루트랑 멀티 루트 컴포넌트의 차이점과, 싱글 루트에서 멀티 루트로 변경하는 작업을 살펴보자.

 

멀티 루트 컴포넌트(Multi-Root Components in Vue)

지난 포스팅에서 사용했떤 BaseInput 컴포넌트를 이어서 사용한다.(여기 코드 참고 https://codesandbox.io/s/purple-sky-umvws?file=/src/components/BaseInput.vue)

 

아래 BaseInput, App 컴포넌트를 살펴보자.

// Baseinput.vue
<template>
  <div>
    <label>{{ label }}</label>
    <input
      v-bind="{
        ...$attrs,
        onInput: (event) => $emit('update:modelValue', event.target.value)
      }"
      :value="modelValue"
    />
  </div>
</template>

<script>
export default {
  inheritAttrs: false,
  props: {
    modelValue: {
      type: [String, Number],
      default: ''
    },
    label: {
      type: String,
      default: ''
    }
  }
}
</script>

App.vue 에서는 인스턴스화하고 BaseInput에 데이터와 이벤트를 바인딩한다.

// App.vue
<template>
  <div id="app">
    <BaseInput
      v-model="email"
      @blur="email = 'blurrr@its.cold'"
      label="Email:"
      type="email"
      class="thicc"
    />

    <pre>{{ email }}</pre>
  </div>
</template>

<script>
import { ref } from 'vue'
import BaseInput from './components/BaseInput'

export default {
  name: 'App',
  components: {
    BaseInput
  },
  setup () {
    const email = ref('')

    return {
      email
    }
  }
}
</script>

위에 코드를 브라우저에서 실행시키면 input에 v-model 바인딩한 결과를 볼 수 있다.

브라우저 화면 결과

Vue3에서 멀티 루트 컴포넌트를 만들기 위해서는 컴포넌트를 감싸고 있는 불필요한 div 태그를 제거하면 된다.

아래 첫번째 이미지는 div 태그 제거전, 두번째 이미지는 div 태그 제거 후 결과 html 코드 결과 이미지이다.

 

div 태그 제거 전
div 태그 제거 후

div 태그 제거 해도 정상적으로 BaseInput 컴포넌트는 렌더링이 된다.

그 다음 해야 될 일은 inheritAttrs를 제거하는 일이다.

Attrs가 바인딩되고 있는 div 태그가 제거 되므로 inheritAttrs 속성이 필요 없어졌다.

// BaseInput.vue
<template>
  <label>{{ label }}</label>
  <input
    v-bind="{
      ...$attrs,
      onInput: (event) => $emit('update:modelValue', event.target.value)
    }"
    :value="modelValue"
  />
</template>

<script>
export default {
  props: {
    modelValue: {
      type: [String, Number],
      default: ''
    },
    label: {
      type: String,
      default: null
    }
  }
}
</script>

$attrs를 사용한 태그에 자동으로 바인딩 된다.

만약 inheritAttrs에도 제거하고 $attrs를 사용하는 곳이 없으면 아래와 같은 경고 메세지가 나온다.

// BaseInput.vue
<template>
  <label>{{ label }}</label>
  <input
    :value="modelValue"
  />
</template>

$attrs이 없는 경우 나오는 경고 메시지

경고 메세지를 잘 읽어보면 부모(App)에서 여러 속성들은 내려주는데 자식(BaseInput) 컴포넌토에 속성들이 위치할 곳이 없다는 메시지이다. 첫번째는 props에 대한 경고 메시지이고 두번째는 emit, event listener에 대한 경고 메시지이다.

Vue3에 새로 추가된 emit 속성을 살펴보자.

 

emit 속성(The emits property)

v-bind="$attrs"를 사용하지 않거나 또는 listeners를 캐치하지 않거나, 동적으로 emits을 하는 경우 Vue에서는 선언을 찾을 수 없다고 경고 메세지를 보내준다. 이러한 경우 emit 속성을 사용한다.

component, setup와 같은 레벨의 속성이다.

 

<script>
export default {
  // 배열로 정의할 경우
  emits: ['peekedIntoTheBox'],
  // 객체로 정의할 경우, validator 추가
  emits: {
    peekedIntoTheBox: payload => {
      return ['dead', 'alive', 'both'].includes(payload)
    }
  }
}
</script>

emits이라는 속성에 peekedIntoTheBox를 선언하면 이 컴포넌트에서는 peekedIntoTheBox를 emit한다고 선언하는 것이다.

이해가 잘 되지 않아 찾아보니 이 컴포넌트에서 어떤 이벤트가 emit되는지 문서화 목적이라고 된 설명을 보았다. 그리고 이걸 통해 validator를 할 수 있는데 해보니까 경고메세지만 나올뿐 이벤트는 정상적으로 작동된다.

따라서, 컴포넌트에 수많은 emit이 있을 경우 복잡하니 간단하게 문서화 정리라고 생각한다.

혹시, 더 좋은 정의를 아시는분이시면 댓글로 알려주세요 

 

결론

이로써 v-model관련하여 Vue2에서 Vue3로 마이그레이션하는 법을 알아봤고 그에 따른 여러가지 속성이나 기능에 대해서 정리해봤다. 아직 실무에서 vue3를 사용하고 있지 않지만 추후 사용시에 이번 포스팅이 도움이 되길 바란다.

 

최종 예제 코드

codesandbox.io/s/purple-sky-umvws?file=/src/components/BaseInput.vue

 

2021/01/22 - [개발일기/Vue.js] - [Vue.js] 새로운 v-model 살펴보기#1 (vue3에서 바뀐 점)

2021/01/28 - [개발일기/Vue.js] - [Vue.js] 새로운 v-model 살펴보기#2 (다수의 v-model 사용하기)

2021/01/31 - [개발일기/Vue.js] - [Vue.js] 새로운 v-model 살펴보기#3 (v-model custom modifiers)

2021/02/07 - [개발일기/Vue.js] - [Vue.js] 새로운 v-model 살펴보기#4 ($attrs, $listeners)

728x90
반응형
LIST