
Vue.js ルーティング演習問題
基本概念まとめ 1. ルーティングの基本設定 2. ナビゲーションガード 3. 動的ルーティング 演習問題(全24問) 初級問題(6問) 基本ルーティング ナビ […]
Vue.jsでは、コンポーネントを組み合わせることでアプリケーションを構築します。親子関係を持つコンポーネント間でデータをやり取りする方法は、Vueの重要なコンセプトです。
単一ファイルコンポーネントが「1つのコンポーネントを1つのファイルに閉じ込める」ことを目的とするのに対し、親子コンポーネントの関係は「コンポーネント間の連携方法」に焦点を当てます。
<!-- ParentComponent.vue -->
<template>
<div>
<ChildComponent :message="parentMessage" @child-event="handleChildEvent" />
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
parentMessage: '親からのメッセージ'
}
},
methods: {
handleChildEvent(data) {
console.log('子から受け取ったデータ:', data);
}
}
}
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<p>親からのメッセージ: {{ message }}</p>
<button @click="sendToParent">親に送信</button>
</div>
</template>
<script>
export default {
props: {
message: String
},
methods: {
sendToParent() {
this.$emit('child-event', '子からのデータ');
}
}
}
</script>
<!-- ParentComponent.vue -->
<template>
<ChildComponent :title="pageTitle" :content="pageContent" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
pageTitle: '親コンポーネントのタイトル',
pageContent: 'これは親から渡されるコンテンツです'
}
}
}
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<h2>{{ title }}</h2>
<p>{{ content }}</p>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
},
content: {
type: String,
default: 'デフォルトコンテンツ'
}
}
}
</script>
// ChildComponent.vue
export default {
props: {
// 基本の型チェック
propA: Number,
// 複数の型を許可
propB: [String, Number],
// 必須項目かつ文字列
propC: {
type: String,
required: true
},
// デフォルト値
propD: {
type: Number,
default: 100
},
// カスタムバリデータ関数
propE: {
validator(value) {
return ['success', 'warning', 'danger'].includes(value);
}
},
// オブジェクトのデフォルト値
propF: {
type: Object,
default() {
return { message: 'hello' };
}
}
}
}
<!-- ChildComponent.vue -->
<template>
<button @click="notifyParent">親に通知</button>
</template>
<script>
export default {
methods: {
notifyParent() {
this.$emit('custom-event', { data: '子からのデータ' });
}
}
}
</script>
<!-- ParentComponent.vue -->
<template>
<ChildComponent @custom-event="handleCustomEvent" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleCustomEvent(payload) {
console.log('子から受け取ったデータ:', payload.data);
}
}
}
</script>
<!-- ParentComponent.vue -->
<template>
<CustomInput v-model="inputValue" />
<p>親コンポーネントの値: {{ inputValue }}</p>
</template>
<script>
import CustomInput from './CustomInput.vue';
export default {
components: {
CustomInput
},
data() {
return {
inputValue: ''
}
}
}
</script>
<!-- CustomInput.vue -->
<template>
<input
type="text"
:value="value"
@input="$emit('input', $event.target.value)"
/>
</template>
<script>
export default {
props: {
value: {
type: String,
required: true
}
}
}
</script>
<!-- ParentComponent.vue -->
<template>
<ChildComponent>
<p>これはスロットに挿入されるコンテンツです</p>
</ChildComponent>
</template>
<!-- ChildComponent.vue -->
<template>
<div class="container">
<h2>子コンポーネントのタイトル</h2>
<slot>デフォルトコンテンツ(親から何も渡されない場合に表示)</slot>
</div>
</template>
<!-- ParentComponent.vue -->
<template>
<LayoutComponent>
<template v-slot:header>
<h1>カスタムヘッダー</h1>
</template>
<template v-slot:default>
<p>メインコンテンツ</p>
</template>
<template v-slot:footer>
<p>カスタムフッター</p>
</template>
</LayoutComponent>
</template>
<!-- LayoutComponent.vue -->
<template>
<div class="layout">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- ParentComponent.vue -->
<template>
<DataList :items="items">
<template v-slot:item="slotProps">
<span>{{ slotProps.item.name }}</span>
<span>{{ slotProps.item.price }}円</span>
</template>
</DataList>
</template>
<script>
import DataList from './DataList.vue';
export default {
components: {
DataList
},
data() {
return {
items: [
{ name: '商品A', price: 1000 },
{ name: '商品B', price: 2000 }
]
}
}
}
</script>
<!-- DataList.vue -->
<template>
<ul>
<li v-for="(item, index) in items" :key="index">
<slot name="item" :item="item"></slot>
</li>
</ul>
</template>
<script>
export default {
props: {
items: {
type: Array,
required: true
}
}
}
</script>
// 祖先コンポーネント
export default {
provide() {
return {
themeData: {
primaryColor: '#42b983',
secondaryColor: '#35495e'
}
}
}
}
// 子孫コンポーネント
export default {
inject: ['themeData'],
created() {
console.log(this.themeData.primaryColor); // #42b983
}
}
<!-- ParentComponent.vue -->
<template>
<ChildComponent ref="child" />
<button @click="callChildMethod">子のメソッドを呼び出す</button>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
callChildMethod() {
this.$refs.child.childMethod();
}
}
}
</script>
<!-- ChildComponent.vue -->
<script>
export default {
methods: {
childMethod() {
console.log('子コンポーネントのメソッドが呼ばれました');
}
}
}
</script>
特徴 | 単一コンポーネント | 親子コンポーネント |
---|---|---|
目的 | 1つの機能を自己完結 | コンポーネント間の連携 |
再利用性 | 限定的 | 高い |
複雑さ | 単純 | 関係性の管理が必要 |
データフロー | 内部のみ | Props/Eventsによる双方向通信 |
適したケース | 小規模なUI部品 | 大規模アプリケーションの構築 |
@my-event
のように命名<!-- Good Practice Example -->
<template>
<SearchResults :items="filteredItems">
<template #item="{ item }">
<ProductCard :product="item" @select="addToCart" />
</template>
</SearchResults>
</template>
<script>
import SearchResults from './SearchResults.vue';
import ProductCard from './ProductCard.vue';
export default {
components: {
SearchResults,
ProductCard
},
data() {
return {
allItems: [],
searchQuery: ''
}
},
computed: {
filteredItems() {
return this.allItems.filter(item =>
item.name.includes(this.searchQuery)
);
}
},
methods: {
addToCart(product) {
// カートに追加するロジック
}
}
}
</script>
Vue.jsの親子コンポーネント間のデータフローは、以下の3つの主要なパターンで構成されます:
単一ファイルコンポーネント(SFC)がコンポーネントの「内部構造」を定義するのに対し、親子コンポーネントの関係は「コンポーネント間の連携方法」を定義します。適切に設計された親子関係は、以下の利点をもたらします:
大規模なVue.jsアプリケーションを構築する際には、これらのデータフローパターンを適切に組み合わせることで、整理された保守性の高いコードベースを維持できます。