最近携わっているVueについての備忘録。

コンポーネント

何度も繰り返し使用するようなセクションを1つの部品として何度も繰り返し使用できるようにする。この再利用可能な部品のことをコンポーネントという。vueではこのように.vueの拡張子をもつ1つのファイルにまとめて記述する、これはシングルファイルコンポーネントという。

コンポーネントの作り方<基本>

①/componentのディレクトリ直下にファイルを作成する。それ自体がコンポーネントとなる。
②App.vueにて、コンポーネントファイルをインポートする。
③インポートした際のインポート名と同じタグを記述する。

<script setup lang="ts">
</script>

<template>
    <section class="box">
        <h4>TEST Component</h4>
        <p>コンポーネント使用方法</p>
    </section>
</template>

<style scoped>
.box {
 border: green 1px dashed;
 margin: 10px;
 padding: 20px;
}
</style>
<script setup lang="ts">
import ComponentsTest from "./components/ComponentsTest.vue";

</script>

<template>
  <section>
    <h2>TITLE</h2>
    <ComponentsTest/>
    <ComponentsTest/>
    <ComponentsTest/>
  </section>
</template>

<style>
  section {
    border: blue 1px solid;
    margin: 10px;
  }
</style>

Props

親コンポーネントから子コンポーネントへデータを受け取る仕組み。

①個々のPropをインターフェースにて定義する(interface名はなんでも良いが基本的にはPropsとする)
②defineProps()関数を実行する(< >内に該当のインターフェイスを記述、型定義)

<script setup lang="ts">
interface Props {
    title: string;
    content: string;
    no: number;
}
defineProps<Props>();
</script>

<template>
    <section class="box">
        <h4>{{ title }}</h4>
        <p>{{ content }}</p>
        <p>{{ no }}</p>
    </section>
</template>

<style scoped>
.box {
 border: green 1px dashed;
 margin: 10px;
}
</style>
<script setup lang="ts">
import OneInfo from "./components/OneInfo.vue";

const title = "変数に入れたタイトル"
const content = "変数に入れたコンテンツ"
const number = "123"
</script>

<template>
  <section>
    <h2>TITLE</h2>
    <OneInfo 
    title="Props使用例"
    content="コンポーネントのコンテンツ"/>
    <OneInfo 
    title="Props使用例2"
    content="コンポーネントのコンテンツ2"/>
    <OneInfo 
    v-bind:title="title"
    v-bind:content="content"/>
    <OneInfo 
    :title="title"
    :no="number"/>
  </section>
</template>

<style>
  section {
    border: blue 1px solid;
    margin: 10px;
    padding: 20px;
  }
</style>

このように定義し、親コンポーネントで渡された値が渡って、それぞれの値が格納され、表示される。
変数に入れた値を表示させるにはv-bindにて定義する。

v-forとPropsの組み合わせ

この例は実務で一番よく見るパターン。

上でのOneInfo.vueはそのまま使用、App.vueでv-forにて回す。

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

const weatherListInit = [
  {id: 1, title:"今日の天気", content:"晴れ"},
  {id: 2, title:"明日の天気", content:"雨"},
  {id: 3, title:"明後日の天気", content:"曇り"},
]
const weatherList = ref(weatherListInit)
</script>

<template>
  <section>
    <h2>TITLE</h2>
    <OneInfo v-for="weather in weatherList"
    :key="weather.id"
    :title="weather.title"
    :content="weather.content"/>
  </section>
</template>

<style>
  section {
    border: blue 1px solid;
    margin: 10px;
    padding: 20px;
  }
</style>

応用編

親から渡されたPropsのデータについて、scriptブロックで処理してから表示する

<script setup lang="ts">
import {ref, computed} from "vue"

interface Props {
    id: number;
    name: string;
    mail: string;
    point: number;
    note: string;
}
const props = defineProps<Props>();

// もしnoteがなかった場合の処理
const aboutNote = computed(() => {
    let note = props.note;
    if (note == undefined) {
        note = "--";
    }
    return note
})

// 個々のPropはreadonlyのため、直接変更できない。
// リアクティブ変数に値をコピーして入れる
const point = ref(props.point)
const pointUp = () => {
    point.value++
}
</script>

<template>
    <section class="box">
        <h4>{{ name }}さんの情報</h4>
        <p>メールアドレス:{{ mail }}</p>
        <p>ポイント:{{ point }}</p>
        <p>備考:{{ aboutNote }}</p>
        <button @click="pointUp">ポイント加算</button>
    </section>
</template>

<style scoped>
.box {
 border: green 1px dashed;
 margin: 10px;
}
</style>

Propsの値の利用の注意点

Propsの値はリアクティブシステムの管理対象となっており、親のコンポーネントで値を変更すると子の値もそのまま変わる。そのため、子コンポーネントでの値を独自に変更してしまうと不具合が起こりかねるため、個々のPropsの値はreadonlyとなっている。子コンポーネント内で変更の可能性のある値は、一旦コピーして変数に入れて使用する。これにより、子コンポーネント独自のリアクティブ変数となり、自由に変更ができるようになる。

デフォルト値の指定

例えば、備考欄に何もなかった場合のデフォルト値を設定したい場合、withDefaults()関数を使用する。

第一引数にdefineProps、第二引数にデフォルト値を指定。

そうすることで、aboutNote()関数の必要がなくなり、Props名をそのままマスタッシュにて使用することが可能となる。

const props = withDefaults(
    defineProps<Props>(),
    {note: "--"}
);

参考書籍:Vue 3 フロントエンド開発の教科書

c.sakyou

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA