基本構成
<script setup lang="ts">
...
</script>
<template>
...
</template>
<style scoped>
...
</style>
リアクティビティ API
<script setup lang="ts">
import { ref, reactive, computed, toRefs } from "vue";
</script>
ref
const count = ref<number>(0);
reactive
interface State {
name: string;
age: number;
}
const state = reactive<State>({
name: "yoshida",
age: 36,
});
toRefs
ref を代入すると参照が切れてリアクティブ性が失われるので toRefs
const { name, age } = toRefs(state);
computed
const upperCaseName = computed(() => state.name.toUpperCase());
Template
変数参照
<template>
<div>名前:{{ state.name }} | {{ upperCaseName }}
<div>名前(toRef) {{ name }}
<div>年齢:{{ state.age }}歳
<div>年齢(toRef) {{ age }}
</template>
ディレクティブ
<template>
<div>{{ count }}
<button type="button" @click="count++">カウントアップ</button>
</template>
Props
親
<template>
<Child label="propsLabel"/>
</template>
子
コンパイラマクロだから import は不要
<script setup lang="ts">
//型定義
interface Props {
label: string;
}
//props
defineProps<Props>();
</script>
<template>
<div>{{ label }}</div>
</template>
初期値を設定する場合
<script setup lang="ts">
//型定義
interface Props {
label: string;
}
//初期値を設定
withDefaults(defineProps<Props>(), { label: "defaultLabel" });
</script>
Emit
親
const changeName = (value: string) => {
state.name = value;
};
const updateName = (value: string) => {
state.name = value;
};
<Child @change="changeName" @update="updateName" label="propsLabel" />
子
//型定義
interface Emits {
(event: "change", value: string): void;
(event: "update", value: string): void;
}
//Emit
const emit = defineEmits<Emits>();
const handleUpdate = () => {
emit("update", "update!");
};
<template>
<input type="text" @input="handleInput" />
<button type="button" @click="handleUpdate">アップデート</button>
</template>
Event
細かいことはおいておいて TypeScript で Event の扱いはややこしいのでとにかく使い方
instanceof で Type Guard
const handleInput = (e: Event) => {
if (e.target instanceof HTMLInputElement) {
emit("change", e.target?.value);
}
};
as でキャスト
const handleInput = (event: Event) => {
emit("change", (event.target as HTMLInputElement).value);
};