리액트를 사용하는 경우 해당 링크의 예제처럼 일반적인 컴포넌트를 만듭니다.
그런데 Vue3는 아무리 찾아봐도 해당 패턴이 없더라구요?
아마 암시적인 Fallthrough Attribute 기능 때문인 것 같습니다.
$attrs 객체는
컴포넌트의 props 또는 emit 옵션(예: 클래스, 스타일, v-on 리스너 등)에 의해
선언되지 않은 모든 속성을 포함합니다.
(주 : props 필터링을 통해 서브 컴포넌트의 계층화, 관심사의 분리에도 활용 가능)
해당 $attrs 객체의 typing을 지원한다면, 좀 더 쉽게 구현을 가이드할 수 있지 않을까요?
Vue3은 runtime-dom.d.ts 파일에서 해당 속성들에 대한 타입을 지원합니다.
(안타깝지만 defineProps내부 객체에서 클래스로 해당 타입을 지원하는 방법은 없는 것 같네요 ㅠ)
intrinsic한 버튼 컴포넌트의 예제를 만들어 보았습니다.
사용법은 다음과 같습니다.
1. 루트 컴포넌트(버튼)의 타입을 확장합니다.
interface Props extends ButtonHTMLAttributes {
msg?: string;
count?: string;
}
2. defineProps를 이용해 타입을 정의합니다.
JS의 경우는 애초에 런타임 타입체킹 밖에 안되니, 문서화로 적절하게 넘어가는 방법밖에 없을 것 같습니다.
TS의 경우는 아래와 같이 작성하면 타입스크립트를 이용해 컴파일러가 props를 생성해 줍니다.
<script setup lang="ts">
import { ButtonHTMLAttributes } from "vue";
interface Props extends ButtonHTMLAttributes {
msg?: string;
count?: string;
}
defineProps<Props>();
</script>
root 컴포넌트가 버튼일 경우 2번에서 끝입니다.
아닐 경우 3,4번을 작업합니다.
3. (optional) inheritAttrs 속성을 false로 설정
해당 설정은 setup과 함께 쓸 수 없습니다. 다른 script 블락을 사용해, 루트 컴포넌트로 fallthrough attributes를 넘기는 디폴트 설정을 끕니다.
<script setup lang="ts">
import { ButtonHTMLAttributes } from "vue";
interface Props extends ButtonHTMLAttributes {
msg?: string;
count?: string;
}
defineProps<Props>();
</script>
<script lang="ts">
export default {
inheritAttrs: false,
};
</script>
4. (optional) 명시적으로 $attrs 바인딩.
useAttrs 컴포저블을 사용해도 됩니다.
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" v-bind="$attrs">
at Children {{ count || "1" }}
</button>
</div>
</template>
예제는 https://github.com/i0boy/vue-generic-component-typescript 에서 확인할 수 있습니다.
codesandbox로 공유할려 했는데, git repo는 embed 지원을 안하네요;
codesandbox의 Vue3지원도 좋지 않습니다.
괜히 리액트를 쓰는게 아닌듯
'FrontEnd' 카테고리의 다른 글
[타입스크립트] Object vs object vs {} (2) | 2022.11.29 |
---|---|
[번역] Rehydration(재수화)의 위험과 문제 해결하기 (0) | 2022.11.28 |
[번역]모듈 번들러는 무엇이며 어떻게 동작하는가? (0) | 2022.11.27 |
Javascript ES module과 순환 참조 해결하기 (0) | 2022.11.27 |
OpenAPI 3.0 튜토리얼 (0) | 2022.11.24 |