1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Vue3]コンポーネントざっくりまとめ

Last updated at Posted at 2023-09-30

概要

App.vueをベースに子コンポーネントをレンダリングする。
html、css、jsを同じ設定で使い回す際に便利な機能。

App.vueをベースにcomponentsディレクトリを作成(ディレクトリ名は自由)。

ディレクトリ構造
src
 ├─ assets
 ├─ components
 ├─ App.vue
 ├─ main.ts
 ...

子コンポーネントの利用方法

componentsディレクトリに子コンポーネントとなるvueファイルを作成してApp.vueでimportする。

Child.vue
<template>
	<section class="box">
		<p>子コンポーネント</p>
	</section>
</template>
App.vue
<script setup lang="ts">
import Child from "./components/Child.vue";
</script>

<template>
	<section>
		<div>App.vue</div>
		<Child />
	</section>
</template>
表示結果
App.vue
子コンポーネント

コンポーネントの独立性

inputとv-modelで表示を確認。
同じ子コンポーネントでも独立して動作する。

Child.vue
<script setup lang="ts">
import {ref} from "vue";

const name = ref("名無し");
</script>

<template>
	<section>
		<p>{{name}}</p>
		<input type="text" v-model="name">
	</section>
</template>

<style scoped>
section {
	color: blue;
}
</style>
.vue
<script setup lang="ts">
import Child from "./components/Child.vue";
</script>

<template>
	<section>
		<div>App.vue</div>
		<Child />
		<Child />
	</section>
</template>
<style>
section {
	color: red;
}
</style>
表示結果
App.vue  // 赤色
hoge  // 青色
[hoge]
huga  // 青色
[fuga]

styleのscopedについて

上記サンプルでは、子コンポーネントのstyleにscopedを付することで子コンポーネント独自のスタイルを設定している。
子コンポーネントのスタイルを削除すれば子コンポーネントの文字色は赤色になり、scopedをなくしてstyleを記述するとApp.vueの文字色が青色になる。

記法
<stype scoped>
...
</style>

親から子へデータを渡す

子コンポーネントでは受け取るデータを定義してdefinePropsを宣言し、親コンポーネントでは子コンポーネントの引数としてデータを渡すことができる。

記法:子コンポーネント
interface Props {
    各Prop名: データ型;
    ...
}
defineProps<Props>();
記法:親コンポーネント
<子コンポーネント名
	各prop名="渡す値"
    ...
Child.vue
<script setup lang="ts">
interface Props {
	title: string;
	content: string;
}
defineProps<Props>();
</script>

<template>
	<section class="box">
		<p>{{title}}</p>
		<p>{{content}}</p>
	</section>
</template>
App.vue
<script setup lang="ts">
import Child from "./components/Child.vue";
</script>

<template>
	<section>
		<div>App.vue</div>
		<Child
			title="Propsのtitle"
			content="propのcontent"/>
	</section>
</template>
表示結果
App.vue
Propsのtitle
propのcontent

親の変数を子に渡す

子コンポーネントは前項と同じで、親コンポーネントの記述が変わる。
値を渡す場合はprop名を引数としていたが、変数を渡す場合はv-bind.prop名となる。
※ 子コンポーネントは前項と同じため省略

記法
<子コンポーネント名 
    v-bind:Prop名="変数名"
    ...    
/>
App.vue
<script setup lang="ts">
import {ref} from "vue";
import Child from "./components/Child.vue";

const propsTitle = ref("propのtitle");
const propsContent = ref("propのcontect");
</script>

<template>
	<section>
		<div>App.vue</div>
		<Child
			v-bind:title="propsTitle"
			v-bind:content="propsContent"/>
	</section>
</template>

表示結果
App.vue
Propsのtitle
propのcontent

v-forとの組み合わせ

子コンポーネントは前項と同じで、親コンポーネントの記述が変わる。
※ 子コンポーネントは前項と同じため省略

記法
<子コンポーネント名 
    v-for=:"要素の変数 in 配列の変数"
    v-bind:Prop名="変数名"
    ...
/>
App.vue
<script setup lang="ts">
import {ref} from "vue";
import Child from "./components/Child.vue";

interface Weather {
	id: number;
	title: string;
	content: string;
}
const weatherListInit = [
	{id: 1, title: "今日の天気", content: "晴でしょう" },
	{id: 2, title: "明日の天気", content: "雨でしょう" },
];
const weatherList = ref(weatherListInit);

</script>

<template>
	<section>
		<h2>App.vue</h2>
		<Child
			v-for="weather in weatherList"
			v-bind:key="weather.id"
			v-bind:title="weather.title"
			v-bind:content="weather.content"/>
	</section>
</template>
表示結果
今日の天気
晴でしょう
明日の天気
雨でしょう

親から渡された値を子で利用する

definePropsで値を受け取った子コンポーネントで値を利用することはできるが、値の直接編集はできないめリアクティブ変数に値をコピーして利用する。

記法
const propsの変数名 = defineProps<型>();
const 子で利用するの変数名 = ref(propsの変数名);
Child.vue
<script setup lang="ts">
import { ref, computed } from "vue";

interface Props {
	points: number;
	note?: string;
}
const props = defineProps<Props>();

const localPoints = ref(props.points);
const localNote = computed(
	(): string => {
		let localNote = props.note ? props.note : "--";
		return localNote;
	}
);

const pointUp = (): void => {
	localPoints.value++;
}
</script>

<template>
	<section class="box">
		<div>
			<span>保有ポイント:{{ localPoints }}</span>
			<span>、備考:{{ localNote }}</span>
		</div>
		<button v-on:click="pointUp">ポイント加算</button>
	</section>
</template>
App.vue
<script setup lang="ts">
import { ref, computed } from "vue";
import Child from "./components/Child.vue";

const lists = [
	{points: 35, note: "note"},
	{points: 56, },
]

const lists = ref(lists);

</script>

<template>
	<section>
		<Child 
		v-for="list in lists"
			 v-bind:points="list.points" v-bind:note="list.note" />
	</section>
</template>

表示結果
保有ポイント:39、備考:note
[ポイント加算]
保有ポイント:56、備考:--
[ポイント加算]

// ポイント加算を押下すると各ポイントが加算される

子から親への通信

子から親への通信はイベント処理を記述する。
流れとしては親コンポーネントに処理とv-onを記述して、子にemitを定義してemitの実行を記述する。

記法:子コンポーネント
interface Emits {
	(event: "イベント名"): void;
};
const emit = defineEmits<Emits>();

処理名 = (): void => {
    emit(”イベント名”)
}
記法:親コンポーネント
・scriptタグ
処理メソッド

・templateタグ
<子コンポーネント名
    v-on:子コンポーネントのイベント名="処理メソッド"
/>
Child.vue
<script setup lang="ts">
interface Emits {
	(event: "createNewVal"): void;
}
const emit = defineEmits<Emits>();

const onNewRandButtonClick = (): void => {
	emit("createNewVal");
}
</script>

<template>
	<section class="box">
		<button v-on:click="onNewRandButtonClick">加算</button>
	</section>
</template>
App.vue
<script setup lang="ts">
import { ref } from "vue";
import Child from "./components/Child.vue";

const parent = ref(0);
const oncreateNewVal = (): void => {
	parent.value++;
}

</script>

<template>
	<section>
		<p>親コンポーネントで値を表示: {{ parent }}</p>
		<Child v-on:createNewVal="oncreateNewVal" />
	</section>
</template>
表示結果
親コンポーネントで値を表示: 0
[加算]

// 加算ボタンで値が1が加算される

emitの第二引数

前述したemitの第二引数に

記法:子コンポーネント
interface Emits {
	(event: "イベント名", 変数:型): void;
};
const emit = defineEmits<Emits>();

処理名 = (): void => {
    emit(”イベント名”, 渡す値)
}
記法:親コンポーネント
・scriptタグ
処理メソッド(子から受け取る値:型)
Child.vue
<script setup lang="ts">

interface Emits {
	(event: "createNewVal", int: number): void;
}
const emit = defineEmits<Emits>();

const onNewRandButtonClick = (): void => {
	emit("createNewVal", 2);
}
</script>

<template>
	<section class="box">
		<button v-on:click="onNewRandButtonClick">加算</button>
	</section>
</template>


App.vue
<script setup lang="ts">
import { ref } from "vue";
import Child from "./components/Child.vue";

const parent = ref(0);
const oncreateNewVal = (int: number): void => {
	parent.value = parent.value + int;
}

</script>

<template>
	<section>
		<p>親コンポーネントで値を表示: {{ parent }}</p>
		<Child v-on:createNewVal="oncreateNewVal" />
	</section>
</template>

ProvideとInject

複数ページのアプリケーションでは、App.vueを親として子コンポーネントを作成していくとコードが複雑になり可読性が低くなるため、今までの子コンポーネントを中間コンポーネントとして孫コンポーネントを作成する。
この場合にデータの受け渡しを行うのが「アプリケーション全体で参照されるデータを提供するprovide」と「階層に関わらずデータの注入ができるinject」という方法になる。
また、inject()の戻り値はunknown型のため、型変換を行う必要がある。

※ サンプルでは親から孫まで同じ型宣言をするのでtype.tsを作成している

表示結果
親コンポーネントで値を表示: 0
[加算]

// 加算ボタンで値が2が加算される
記法:provide
provide("Provide名", 値);
記法:inject
変数の宣言 = inject("provide名"); 

provide("Provide名", 値);
App.vue
<script setup lang="ts">
import { provide } from "vue";
import BaseSection from "./components/BaseSection.vue";
import type { propsList } from "@/components/type";

const lists: propsList[] = [
	{ id: 1, points: 35, note: "note" },
	{ id: 2, points: 24 },
];

const childTitle: string = "孫コンポーネント";

provide("lists", lists);
provide("childTitle", childTitle);
</script>

<template>
	<BaseSection />
</template>

BaseSection.vue
<script setup lang="ts">
import { ref, inject } from "vue";
import ChildSection from "./Child.vue";
import type { propsList } from "@/components/type";

const lists = inject("lists") as propsList[];
// const lists = inject<propsList[]>("lists");  // 型指定でもOK
</script>

<template>
	<section>
		<p>base section</p>
		<ChildSection v-for="list in lists"
		 v-bind:id="list.id"
		 v-bind:points="list.points"
		 v-bind:note="list.note"
		/>
	</section>
</template>

ChildSection.vue
<script setup lang="ts">
import {computed, inject} from "vue";
import type { title, propsList } from "@/components/type";

const list = defineProps<propsList>();
const localNote = computed(
	(): string => {
		let localNote = list.note ? list.note : "--";
		return localNote;
	}
);
const childTitle = inject<title>("childTitle");

</script>

<template>
<section>{{ childTitle }}</section>
<section>
		<span>id:{{ list.id }}、point:{{ list.points }}、備考: {{ localNote }}</span>
	</section>
</template>

type.ts
export type propsList = {
    id: number;
    points: number;
    note?: string;
};

表示結果
base section
孫コンポーネント
id:1、point:35、備考: note
孫コンポーネント
id:2、point:24、備考: --
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?