💬
Vue.jsでRest Typeを使って、propsの一部をそのまま子コンポーネントに流す
あるコンポーネントをあるコンポーネントでラップして使いたいケースがたまにあります。
単純にAコンポーネントのエイリアスとしてBコンポーネントを作る場合、$propsをv-bindに割り当てればすみます。
types.ts:
export interface AProps {
a: string;
b: number;
c?: boolean;
}
A.vue:
<script setup lang="ts">
import { AProps } from "./types";
const props = defineProps<AProps>();
</script>
<template>
<div>{{ props.a }}</div>
<div>{{ props.b }}</div>
<div>{{ props.c }}</div>
</template>
B.vue:
<script setup lang="ts">
import { AProps } from "./types";
import A from "./A.vue";
const props = defineProps<AProps>();
</script>
<template>
<A v-bind="$props" />
</template>
しかし、BコンポーネントでBコンポーネント独自のpropsを受け取りつつ、AコンポーネントのpropsはAコンポーネントに流したい場合はどうすればよいでしょうか。
単純な解決として、Aコンポーネントで使うpropsを一つ一つ記述して渡します。
<script setup lang="ts">
import { AProps } from "./types";
import A from "./A.vue";
const props = defineProps<AProps & { d: string }>();
</script>
<template>
<div>
{{ props.d }}
<B :a="props.a" :b="props.b" :c="props.c" />
</div>
</template>
しかしこれではAコンポーネントに変更が入った場合に追従がめんどうです。
分割代入で、Bコンポーネント独自のpropsを受け取りつつ、rest typeでAコンポーネントのpropsを受け取るという手法が思い浮かびます。
import { AProps } from "./types";
import A from "./A.vue";
const props = defineProps<AProps & { d: string }>();
const { d, ...rest } = props;
しかしこれではリアクティビティが失われてしまいます。
そこでcomputedを使います。computedによって、Bに指定されたpropが変更された場合でもAは変更されます。
<script setup lang="ts">
import { computed } from "vue";
import { AProps } from "./types";
import A from "./A.vue";
const props = defineProps<AProps & { d: string }>();
const rest = computed(() => {
const { d: _, ...rest } = props;
return rest;
});
</script>
<template>
<div>
{{ props.d }}
<A v-bind="rest"></A>
</div>
</template>
この方法なら、Aのpropsに変更が入った場合でも自動でBは追従することができます。ただし、Bコンポーネントのpropsが増えた場合にrestの取り出しが面倒です。なにか別の良いやり方があればコメントお待ちしています。
Discussion