【vue】 コンポーネントの使い方

vueにおけるコンポーネントは、オブジェクト指向におけるクラスとに似ています。コンポーネントを使うことでクラスのように関心を切り分けることができます。

使い方

コンポーネントはクラスのようなものです。コンポーネントを作ることで関心を切り分けたり、複製して効率よく使い回すことができます。

コンポーネントを作る

例えば、クリックしたらその回数をカウントするコンポーネントを作成してみます。

const app = Vue.createApp({
    // ...
});

// ボタンのコンポーネント
app.component('button-counter', {
    data() {
        return {
            count: 0,
        }
    },
    methods: {
        addCount(v) {
            this.count += 0;
        }
    },
    template: `
<div>
    <button @click="addCount(2)">クリック!</button>
    <p>クリック回数: {{ count }}</p>
</div>
    `,
});

templateの中身は文字列で、バッククォートで囲むことで複数行書けるようにしています。

コンポーネントには、普通にcreateAppに書くものとおおよそ同じ内容を書きますが、引数とtemplateが追加されています。

app.componentの第1引数にはコンポーネントの名前を、第2引数にはそのコンポーネントの中身を記入します。

templateには、実際に表示するHTMLを書きます。v-forなども同様に使用できます。

templateの中身のHTMLは、必ず1つの要素である必要はありません。

しかし、コンポーネントで表示している部分を明確にすることで、CSSの適用がしやすくなったり、DOMの構造がわかりやすくなります。

コンポーネントを使う

コンポーネントは、HTML側でapp.componentに設定した名前と同じ名前で、HTMLタグに用に書きます。

<div id="app">
    <button-counter></button-counter>
</div>

これは、クラスのインスタンスを作成しているのと似ています。表示される際は、上で設定したtemplateの内容 (v-forなどはコンポーネント内で処理される) が表示されます。

そのため、複数の同じコンポーネントを配置できます。

<div id="app">
    <button-counter></button-counter>
    <button-counter></button-counter>
    <button-counter></button-counter>
</div>

そして、それぞれが持っているdataの値は、クラスと同じように独立しています。

See the Pen vue component2 by Totori (@souki202) on CodePen.

このように、vueにおけるコンポーネントとその利用は、クラスの宣言とインスタンスの作成の概念に似ていますね。

コンポーネントの図

propsで値を渡す

クラスを作るときにコンストラクタに引数を渡せるように、コンポーネントをHTMLにタグで書く際に値を渡すことができます。

受け取る側は、propsを用意して変数の箱を作ります。propsに入ってきた値は、dataと同じように読み出すことができます。

app.component("button-counter", {
    props: ["addValue"],
    data() {
        return {
            count: 0,
        }
    },
    methods: {
        addCount() {
            // 送る側がv-bindでないため, 文字列になっている
            this.count += Number(this.addValue);
        }
    },
    template: `...`,
});
<button-counter add-value="3"></button-counter>

ここで、渡すときの注意点として、propsはスネークケース(add_value)やキャメルケース(addValue)で書きますが、キャメルケースの場合、HTMLでは必ず-で区切る (ケバブケース) ようにする必要があります。

propshtml
foofoo
fooBarfoo-bar
foo_barfoo_bar
app.component("button-counter", {
    props: ["addValue"],
    // ...
});
<!-- ケバブケースになる -->
<button-counter add-value="3"></button-counter>

props側の名前がスネークケースの場合、HTML側も<button-counter add_value="3"></button-counter>のようにスネークケースで書きます。

v-bindで値を渡す

v-bindを利用して渡すこともできます。この場合、propsで送る値と受け取る値の型は一致します。

オブジェクトも渡せます。

<div id="app">
    <div v-for="i in 3">
        <button-counter :addValue="i"></button-counter>
    </div>
</div>

See the Pen by Totori (@souki202) on CodePen.

propsの制約

propsにある値はdataと同じように読み出す事はできますが、更新することはできません。つまり親から子の一方通行です。propsの値をコンポーネント内でv-modelに利用したりなどはできません。

子コンポーネント内だけで更新したい場合、そのコンポーネント内でdataに代入して使う方法があります。ここで、配列やオブジェクトの場合は参照渡しされるため、その中の値を書き換えると親や他に渡したコンポーネントの値も連動してしまうので注意してください。

app.component("button-counter", {
    props: ["value"],
    data() {
        return {
            // ここでdataに入れて, 子コンポーネント内のメンバ変数のような感じにする
            myValue: this.value
        };
    },
    methods: {
        f() {
            <!-- this.value = 10; // propsの変数に代入はNG!! -->
            this.myValue = 10; // dataの変数なのでOK 
        }
    }
    // ...
}

配列を渡した場合は参照が渡されるため、連動して変わってしまう例です。

See the Pen vue component prop3 by Totori (@souki202) on CodePen.

親コンポーネント側の値が書き換わるようにもできますが、それは別の場所でpropsを掘り下げます。

コンポーネントがコンポーネントを持つ

クラスが別のクラスのインスタンスを持てるように、コンポーネントが別のコンポーネントを表示することができます。

app.component("header-component", {
    data() {
      return {};
    },
    template: `
<header>
    <p>header component</p>
    <!-- header-componentがbutton-counter呼び出し -->
    <button-counter v-for="i in 3" :addValue="i"></button-counter>
</header>
`
});

See the Pen vue component3 by Totori (@souki202) on CodePen.

まとめ

コンポーネントは、クラスのような感覚で扱うことができます。しかし、このままでは使いづらいので、次はコンポーネントを1つのファイルにして管理してみましょう。

1つのvueファイルにコンポーネントを切り出したい場合は、下の記事で説明しています。

vue component thumb

役に立ったらシェアしよう!