Vue.js 超入門3.4+ 第15章 おまけ1 - 親子の会話
https://scrapbox.io/files/66812ac240163e001db7bdf2.png
第15章 おまけ1 - 親子の会話
親コンポーネントと子コンポーネント間でのいくつかのやり取り
15.1 親子間の双方向バインディング
defineModel v3.4からstable
登録画面のフォーム部分(FormInput)をコンポーネントに分離した場合
親コンポーネント
code:typescript
<script setup lang="ts">
import { reactive } from 'vue';
import FormInput from './FormInput.vue';
type User = {
id: string;
};
const onSubmit = () => {
// 登録処理
};
</script>
<template>
<form @submit.prevent="onSubmit">
<FormImput />
<button type="submit">登録</button>
</form>
</template>
子コンポーネント
code:typescript
<template>
<div>名前: <input type="text" /></div>
</template>
v3.3の場合
modelValueという特別な変数を通してやり取りしていた、v3.4になると扱いやすくなった
code:typescript
<script setup lang="ts">
const userData = reactive<User>({
id: ''
});
</script>
<template>
<form @submit.prevent="onSubmit">
<FormImput v-model="userData.id" /> <!-- v-modelを追加 -->
<button type="submit">登録</button>
</form>
</template>
code:typescript
<script setup lang="ts">
</script>
<template>
<div>
ID:
<input
type="text"
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
v3.4の場合
defineModelを使うとより簡単に書ける、emitとかいらない?
defineModelはVueのマクロであり、コンパイル時にv3.3の書き方に展開される
code:typescript
<script setup lang="ts">
const model = defineModel();
</script>
<template>
<div>
ID:
<input
type="text"
v-model="model"
/>
</div>
</template>
15.2 複数のバインディング
v-modelに引数を渡すことによって、複数のバインティングを実現できる
例えば、子コンポーネントに名前項目を追加してバインディングしてみる
code:typescript
<script setup lang="ts">
type User = {
id: string;
name: string;
};
const userData = reactive<User>({
id: '',
name: ''
});
</script>
<template>
<form @submit.prevent="onSubmit"> // フォーム送信時にページをリロードせずに、Vue.jsで定義した処理を実行するための記述
<FormInput
v-model:user-id="userData.id"
v-model:user-name="userData.name"
/>
<button type="submit">登録</button>
</form>
</template>
親の方で、v-modelの後にコロンと名前を設定する
子の方では、親から呼び出されたv-modelの名前を使って値を取得する
code:typescript
<script setup lang="ts">
const userId = defineModel('userId');
const userName = defineModel('userName');
</script>
<template>
<div>
ID: <input type="text" v-model="userId" />
<br />
名前: <input type="text" v-model="userName" />
</div>
</template>
ポイント
命名規則の変換: 親でuser-id(ケバブケース)→子でuserId(キャメルケース)
双方向バインディング: 子コンポーネントで値が変更されると、親のuserDataも自動的に更新される
複数の値を同時管理: 1つのコンポーネントで複数のフィールドを扱える
15.3 子コンポーネントの関数を使う
子コンポーネントにkey属性を与えると、そのkeyが変更されることにより、強制的に再マウントすることができます。
以下の例では、変数 count を key として、countが変更(加算)されるたびに、FormCountコンポーネントが再マウントされています
親コンポーネント
code:typescript
<script setup lang="ts">
import { ref } from 'vue';
import FormCount from './FormCount.vue';
const count = ref(0);
const increment = () => {
count.value++;
};
</script>
<template>
<div>
<FormCount :count="count" :key="count" />
<button type="button" @click="increment">加算</button>
</div>
</template>
子コンポーネント FormCount.vue
code:typescript
<script setup lang="ts">
import { ref } from 'vue';
const props = defineProps<{ count?: number }>();
const childCount = ref(props.count);
</script>
<template>
<div>props.count →{{ props.count }}</div>
<div>childCount → {{ childCount }}</div>
</template>