Vue에서도 React { ...props } 처럼 하위 컴포넌트로의 속성 전달하기
목차
· 글을 시작하며
· useAttrs()
· 폴스루 속성 (Fall Through Attributes)
글을 시작하며
어쩌다보니 백오피스 프론트엔드 메인 포지션을 맡게되었고, 전반적인 백오피스의 컴포넌트들이 Vuetify에 종속이 되어있다. Vue2에서 Vue3로 마이그레이션을 진행하면서 라이브러리 버전업에 골머리를 앓았는데, 이참에 Vuetify의 종속성을 제거하고자 대부분의 Select, Input, Button과 같은 원초적인 컴포넌트들의 경우에는 자체 도메인 컴포넌트로 커스텀하게 만들고싶었다. 기존 백오피스 코드는 props를 하나하나 명시를 해주었기 때문에, 개발자로부터 상속받는 상태 값이 많아지면 많아질 수록 그만큼 많은 명시를 해줘야한다는 번거로움을 발견했다. Vue는 React처럼 ...props 형태로 상태 값을 유동적으로 받을 수 있는 경우가 없는지 궁금해서 이렇게 포스팅을 진행하게 되었다.
useAttrs()
Vue에서는 useAttrs() 또는 setup 컨택스트를 통해 attrs에 접근할 수 있다. 부모 컴포넌트에서 자식 컴포넌트로 여러 속성을 쉽게 전달할 때 유용하다.
- useAttrs를 사용하여 attrs에 접근 가능
- v-bind="attrs"로 모든 속성을 한번에 바인딩 가능
- props로 선언된 속성은 attrs에 포함되지 않음
- v-on 이벤트 리스너도 attrs에 포함됨
- 컴포넌트 재사용성 향상에 매우 유용
- inheritAttrs 옵션으로 속성 상속 제어 가능
- inheritAttrs 옵션이 true일 경우, 자식 컴포넌트는 부모 컴포넌트로부터 전달된 모든 속성을 상속 받음
- inheritAttrs 옵션이 false로 설정된 경우에는 자식 컴포넌트는 명시적으로 props 또는 v-bind를 사용하여 전달되지 않은 속성은 상속받지 않음
여기서 Vue가 제시하는 속성은 폴스루 속성이다.
폴스루 속성 (Fall Through Attributes)
<script setup lang="ts">
import MyComponent from "./components/MyComponent.vue";
</script>
<template>
<MyComponent
c="C"
d="D" />
</template>
만약 자식 요소가 하나만 있는 경우에는 자동으로 컴포넌트 최상위 요소에 연결 (상속, Inheritance) 된다.
<script setup lang="ts">
// 사용하지 않은 Props로 정의한 속성 a와 b
defineProps<{
a?: string
b?: number
}>()
</script>
<template>
<div>
<span> Hello! </span>
</div>
</template>
// 위 코드의 출력 결과
<div c="C" d="D">
<span> Hello! </span>
</div>
반대로 최상위 요소가 2개 이상인 경우, 폴스루 속성은 어떤 요소에도 연결되지 않는다.
<script setup lang="ts">
defineProps<{
a?: string
b?: number
}>()
</script>
<template>
<div>
<span> Hello! </span>
</div>
<h2> Hi </h2>
<p> Greetings </p>
</template>
// 위 코드의 출력 결과
<div>
<span> Hello! </span>
</div>
<h2> Hi </h2>
<p> Greetings </p>
필요한 경우 최상위 요소가 하나더라도 폴스루 속성이 자동으로 연결되지 않도록 처리할 수 있다.
<script setup lang="ts">
defineProps<{
a?: string
b?: number
}>()
defineOptions({
inheritAttrs: false
})
</script>
<template>
<div>
<span> Hello! </span>
</div>
</template>
폴스루 속성은 스크립트에서도 접근할 수 있다. 그리고 attrs($attrs) 객체는 onClick과 같이 이벤트를 포함한다. (단, Props는 포함하지 않는다)
<script setup lang="ts">
import MyComponent from "./components/MyComponent.vue";
function clickHandler() {
console.log("Its clicked!");
}
</script>
<template>
<MyComponent
a="A"
:b="2"
c="C"
d="D"
@click="clickHandler"
/>
</template>
// MyComponent.vue
<script setup lang="ts">
import { useAttrs } from 'vue';
defineProps<{
a?: string
b?: number
}>();
const attrs = useAttrs();
console.log(attrs);
</script>
<template>
<div>
<span> Hello! </span>
</div>
</template>
// 위 코드의 출력 결과
{
c: "C"
d: "D"
onClick: f clickHandler()
}