コンポーネントを深く理解する(後半)
ライフサイクルフック
Vue3では、以下のライフサイクルフックをComposition API内で使用できます。
- onBeforeMount: コンポーネントがマウントされる前に実行されます。
- onMounted: コンポーネントがマウントされた後に実行されます。
- onBeforeUpdate: リアクティブな依存関係が更新され、かつDOMが再レンダリングされる前に実行されます。
- onUpdated: リアクティブな依存関係が更新され、DOMが再レンダリングされた後に実行されます。
- onBeforeUnmount: コンポーネントがアンマウントされる前に実行されます。
- onUnmounted: コンポーネントがアンマウントされた後に実行されます。
- onErrorCaptured: 子孫コンポーネントでエラーがキャプチャされたときに実行されます。
- onRenderTracked: レンダリング中にリアクティブなプロパティがトラックされたときに実行されます。
- onRenderTriggered: レンダリングがトリガーされたとき(リアクティブな依存関係が変更されたとき)に実行されます。
各フックの具体的な使用例を見ていきましょう。
<script setup>
import {
onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted, onErrorCaptured,
onRenderTracked, onRenderTriggered, ref
} from 'vue';
const data = ref('初期データ');
onBeforeMount(() => {
console.log('マウント前');
});
onMounted(() => {
console.log('マウント後');
data.value = '更新データ';
});
onBeforeUpdate(() => {
console.log('更新前');
});
onUpdated(() => {
console.log('更新後');
});
onBeforeUnmount(() => {
console.log('アンマウント前');
});
onUnmounted(() => {
console.log('アンマウント後');
});
onErrorCaptured((error, instance, info) => {
console.error('エラーキャプチャ', error, instance, info);
});
onRenderTracked((event) => {
console.log('レンダートラック', event);
});
onRenderTriggered((event) => {
console.log('レンダートリガー', event);
});
</script>
Computedプロパティ
computed
プロパティは、依存するデータに基づいて値を計算するために使われます。この計算された値はキャッシュされ、依存するデータが変更されたときにのみ再計算されます。
基本的な使用例
この例では、firstName
とlastName
が変更されたときにのみfullName
が再計算されます。
<script setup>
import { ref, computed } from 'vue';
// リアクティブな参照
const firstName = ref('John');
const lastName = ref('Doe');
// computedプロパティを使ってフルネームを計算
const fullName = computed(() => {
return `${firstName.value} ${lastName.value}`;
});
</script>
<template>
<div>{{ fullName }}</div>
</template>
watchとwatchEffect
watch
機能は、指定されたリアクティブな参照やリアクティブなオブジェクトのプロパティが変更されるたびに、指定されたコールバック関数を実行します。これは、データの変更に応じて非同期操作を行う場合や、特定のデータの変更に基づいて何らかの副作用をトリガーする場合に便利です。
watchの使用例
この例ではcount
の値が変更されるたびにコンソールにメッセージが表示されます。
<script setup>
import { ref, watch } from 'vue';
const count = ref(0);
// countが変更されるたびに呼び出されるwatcher
watch(count, (newValue, oldValue) => {
console.log(`countが${oldValue}から${newValue}に変更されました`);
});
</script>
<template>
<button @click="count++">Count: {{ count }}</button>
</template>
watchの応用: 複数のソースを監視
watch
は、複数のリアクティブな参照やリアクティブなオブジェクトのプロパティを同時に監視することもできます。
この例では、firstName
またはlastName
のどちらかが変更されると、変更前後の名前がコンソールに表示されます。
<script setup>
import { ref, watch } from 'vue';
const firstName = ref('John');
const lastName = ref('Doe');
// firstNameとlastNameのどちらかが変更されたときに実行
watch([firstName, lastName], ([newFirstName, newLastName], [oldFirstName, oldLastName]) => {
console.log(`名前が${oldFirstName} ${oldLastName}から${newFirstName} ${newLastName}に変更されました`);
});
</script>
watchEffectの使用例
watchEffect
は、指定されたコールバック関数内で使用されるすべてのリアクティブな状態を自動的に追跡し、それらのいずれかが変更されるとコールバック関数を再実行します。watchEffect
は、依存関係を明示的に指定する必要がないため、watch
よりも宣言的なケースで便利です。
<script setup>
import { ref, watchEffect } from 'vue';
const count = ref(0);
watchEffect(() => {
console.log(`countの現在値は${count.value}です`);
});
</script>
<template>
<button @click="count++">Count: {{ count }}</button>
</template>
この例では、count
の値が変わるたびに、watchEffect
内のコールバックが自動的に再実行され、最新のcount
の値がコンソールに出力されます。
watchとwatchEffectの違い
- 明示的な依存関係の指定:
watch
では、監視するリアクティブな参照やオブジェクトのプロパティを明示的に指定する必要があります。これに対して、watchEffect
はコールバック関数内でアクセスされるすべてのリアクティブな状態を自動的に追跡します。 - 遅延実行:
watch
は、指定された依存関係が変更された時にのみコールバック関数を実行します。一方で、watchEffect
は初期実行時に自動的にコールバック関数を実行し、依存するリアクティブな状態のいずれかが変更されると再実行します。 - 応答性:
watchEffect
は、関数内で参照されるすべてのリアクティブなプロパティを自動的に監視するため、より動的な依存関係の追跡に適しています。watch
は、より制御が必要な場合や、特定のデータの変更を監視する場合に適しています。 - 柔軟性と制御:
watch
は、古い値と新しい値をコールバック関数の引数として提供し、さらには監視のオプション(深い監視、即時実行など)を細かく設定できるため、より柔軟な制御が可能です。watchEffect
は自動的な依存関係の追跡に重点を置いており、細かい制御は少ないですが、使いやすさに優れています。
refとreactiveとその違い
Vue 3のリアクティブシステムは、アプリケーションの状態管理を効果的に行うための核心的な機能です。このシステムの中心にあるのがref
とreactive
です。これらはリアクティブなデータを作成するために使用されますが、用途と動作の仕方に違いがあります。ここでは、ref
とreactive
の違いを具体的なコード例を交えて解説します。
ref
ref
は、プリミティブ型(文字列、数値、ブール値など)をリアクティブなデータとして扱うために使用されます。ref
によって作成されたデータにアクセスするには.value
プロパティを通じて行います。
この例では、message
はプリミティブな文字列値をリアクティブに保持しており、.value
を通じてその値にアクセスしています。
<script setup>
import { ref } from 'vue';
// プリミティブ型のリアクティブなデータ
const message = ref('Hello Vue 3!');
// .valueを通じてアクセスおよび更新
function updateMessage() {
message.value = 'Updated message';
}
</script>
<template>
<div>{{ message }}</div>
<button @click="updateMessage">Update Message</button>
</template>
reactive
reactive
は、オブジェクトや配列などの複合型をリアクティブなデータとして扱うために使用されます。reactive
によって作成されたデータは、直接的にアクセスおよび更新が可能です。
この例では、state
は複数のプロパティを含むオブジェクトをリアクティブに保持しており、直接的にこれらのプロパティにアクセスして更新しています。
<script setup>
import { reactive } from 'vue';
// オブジェクトのリアクティブなデータ
const state = reactive({
count: 0,
message: 'Hello'
});
// 直接的にアクセスおよび更新
function increment() {
state.count++;
}
function updateMessage() {
state.message = 'Updated message';
}
</script>
<template>
<div>{{ state.count }} - {{ state.message }}</div>
<button @click="increment">Increment</button>
<button @click="updateMessage">Update Message</button>
</template>
refとreactiveの違い
- データ型:
ref
はプリミティブ型をリアクティブにするために使用され、.value
を通じてアクセスします。reactive
はオブジェクトや配列などの複合型をリアクティブにします。 - アクセス方法:
ref
作成されたデータは.value
プロパティを介してアクセスされますが、reactive
によってリアクティブ化されたオブジェクトは直接操作が可能です。 - テンプレート内での振る舞い: テンプレート内で
ref
から作成されたリアクティブなデータを使用する際は.value
を省略できますが、reactive
オブジェクトはそのまま使用します。
Teleportとフラグメント
Teleport
Vue3のTeleport機能は、コンポーネントのHTMLを異なる場所にレンダリングするために使用されます。これは、ページ全体のどこにでもモーダルウィンドウやトーストメッセージを表示したい場合に特に便利です。以下は、Teleportを使用してモーダルコンポーネントをページのbody
タグ内に動的に挿入する簡単な例です。
app.vueでは、モーダルを開閉するボタンと、モーダルコンポーネントへのTeleportを含みます。ボタンをクリックすると、ModalComponent.vue
が <teleport to="body">
を通じて画面に表示されます。
ModalComponent.vueは、モーダルのオーバーレイとウィンドウを提供し、背景や閉じるボタンをクリックすることでモーダルを閉じることができます。
<script setup>
import ModalComponent from './ModalComponent.vue';
const showModal = ref(false)
</script>
<template>
<div>
<button @click="showModal = true">Open Modal</button>
<teleport to="body">
<ModalComponent v-if="showModal" @close="showModal = false" />
</teleport>
</div>
</template>
フラグメント
Vue3以前のバージョンでは、各Vueコンポーネントは単一のルート要素を持つ必要がありました。しかし、Vue 3.0からはこの制限が撤廃され、コンポーネントが複数のルートノードを持つことができるようになりました。これは「フラグメント」として知られており、コンポーネントのテンプレートで複数の要素を直接返すことができるようになることを意味します。これにより、DOMの構造をより柔軟に扱うことが可能になりました。