Vue.jsで複数のコンポーネントを利用してアプリケーションを構築していく場合はコンポーネント間でデータの受け渡しを行えるPropsの知識が不可欠になります。本文書はPropsのみに注目しシンプルなコードを利用しながら詳細に説明を行っています。本文書を読み終えるとPropsの使い方をマスターすることができます。

本文書ではVue3を利用して動作確認を行っています。(まだbeta版ですがvue createコマンドでVue3を選択することができます。)

Propsとは

Propsを利用することで親コンポーネントから子コンポーネントに文字列、数値、配列やオブジェクトなどの値を渡すことができます。値を渡すというので最初に思い浮かぶのは関数かと思います。関数に値を渡す時は引数を利用するのと同様にPropsでも引数のようにコンポーネントに対して値を渡すことができます。また関数に値を与えることで決められた処理を行うようにコンポーネントへPropsを渡すことでコンポーネント内でも決められた処理を行うことができます。

コンポーネントが正しく処理を行うためには正しいデータをコンポーネントに渡す必要があります。Vue.jsでは渡す値のデータタイプやデフォルト値を設定することでバグにつながらない正しいデータを渡す仕組みを備えています。

Propsを利用したシンプルな例

propsをどのように利用するかをシンプルな例を使って確認していきましょう。

まずコンポーネントProduct.vueを作成します。Product.vueコンポーネントはpropsで商品を持つpropsのnameを受け取り表示させるだけのシンプルなものです。ここでは1つのpropsのみ受け取っていますが、複数のpropsも受け取ることができます。


<template>
  <h3>{{ name }}</h3>
</template>

<script>
export default {
  name: "Product",
  props: ["name"],
};
</script>

<style>
</style>

作成したProductコンポーネントを利用するためには別のコンポーネント(親)でProductコンポーネント(子)をimportして”name”でトートバッグという値を渡しています。


<template>
  <div id="app">
    <Product name="トートバッグ" />
  </div>
</template>

<script>
import Product from "./components/Product.vue";

export default {
  name: "App",
  components: {
    Product,
  },
};
</script>

ブラウザ上には親コンポーネントから子コンポーネント(Product.vue)に渡したnameの値であるトートバッグが表示されます。

Propsで渡した値を表示
Propsで渡した値を表示

コンポーネントの再利用

コンポーネントは再利用できるので、1つのコンポーネントの中で複数のProductコンポーネントを利用することができます。nameに異なる値を渡すことで同じコンポーネントでも異なる内容を表示させることができます。


<Product name="トートバッグ" />
<Product name="バックパック" />
複数のコンポーネントを利用
複数のコンポーネントを利用

複数のコンポーネントを利用する場合、Product.vueコンポーネントでの変更や設定は何も必要ありません。

v-bindを利用した場合

nameに直接値を入れていましたが、Vueのデータプロパティを利用して値を渡することもできあす。その際はバインドをする必要があるのでnameの前にv-bindを設定します。追加したデータプロパティproductNameにトートバッグを設定しています。


<template>
  <div id="app">
    <Product v-bind:name="productName" />
    <Product name="バックパック" />
  </div>
</template>

<script>
import Product from "./components/Product.vue";

export default {
  name: "App",
  components: {
    Product,
  },
  data() {
    return {
      productName: "トートバッグ",
    };
  },
};
</script>

productNmaeにトートバッグを設定しているのでブラウザに表示される内容に変化はありません。

v-bindを利用
v-bindを利用
v-bind:nameと記述していますが、短縮形の:nameも利用することができます。

PropsのTypeを設定

使用した例があまりにも簡単であるため渡す値に文字列以外のものを渡すことは考えられないかもしれません。しかしコードが複雑になれば、誤って文字列で渡すべきところを数値で渡したり、オブジェクトを渡したりすることもあります。ここまでの設定では渡したデータがそのまま表示されてしまいます。

vue.jsでは上記のような問題を解消するためpropsにはどのような値が入るかtypeによってチェックを行うことができます。Productコンポーネントのpropsのnameには文字列が入るのでまずtypeにStringを設定します。Stringの先頭文字は大文字です小文字にするとエラーになります。これでnameにはString(文字列)が入ってくることを宣言しています。


export default {
  name: "Product",
  props: {
    name: {
      type: String,
    },
  },
};
typeにStringの他にArray, Number, Objcet, Booleanなどがあります。

再度ブラウザで確認すると先ほどと変化はありません。

次に渡す値を文字列ではなく数字で渡すことでどのような変化があるのか確認しましょう。v-bindで設定していたデータプロパティproductNameの値を文字列から数字に変更します。


data() {
  return {
    productName: 2,
  };
},

ブラウザをだけを数字が表示されますしかしコンソールログを見ると警告が表示されています。

コンソールにWarning(警告)表示
コンソールにWarning(警告)表示

警告ではtypeチェックが行われString(文字列)ではなく数字が渡されていると警告で知らせてくれています。

このようにtypeを利用することで値の型のチェックを行うことができます。警告により文字列以外の値が渡されたことを即座に理解することができます。

下記のように直接nameに入れる値を数字にしても2を文字列として渡しているので何も警告が出ません。数字を渡したのに警告が出ないと慌てないでください。


<Product name="2" />

Propsのデフォルト値の設定

子コンポーネントに設定したpropsが設定されなかった場合にデフォルト値を設定しておくことでデフォルト値を利用することができます。


  props: {
    name: {
      type: String,
      default: "バッグ",
    },
  },

default値を設定するとコンポーネントにnameを設定しない場合にデフォルト値が利用されます。


<Product v-bind:name="productName" />
<Product />
コンポーネントにpropsを設定しない場合
コンポーネントにpropsを設定しない場合

もしデフォルト値を設定しなかった場合にどのようになるかの確認も行っておきましょう。


  props: {
    name: {
      type: String,
    },
  },

defaultを設定しない場合は警告などのメッセージも表示されません。ブラウザ上にはpropsで何も値を渡していないので何も表示されません。トートバッグは別のコンポーネントに設定を行った値です。

デフォルト設定がない場合
デフォルト設定がない場合

Requiredを設定し必須かどうかを設定

requiredを設定することでそのpropsが必須かどうかの設定を行うことができます。先ほどの続きでdefaultを設定なしで、コンポーネントタグの中にpropを設定していない状態でrequiredを設定してみましょう。requiredはtrueまたはfalseの値を設定することができます。


<Product v-bind:name="productName" />
<Product />

propsのnameに必須を設定するためにrequiredをtrueに設定します。


  props: {
    name: {
      type: String,
      required: true,
    },
  },

ブラウザのコンソールを確認すると今回は必須なのにnameが与えられていないということで警告が表示されます。警告ではpropの”name”がないという表示されています。

propsにrequiredを与えて動作確認
propsにrequiredを与えて動作確認

requiredをtrueに設定することで警告が表示されたのでこのことからrequiredを設定していない場合はrequiredがfalseに設定されていることが予想できます。

defaultとrequired

defaultを設定している場合はpropsが与えられないとdefaultに設定した値が利用されるのでreuqiredを設定する必要はありません。つまり下記のようにdefaultとrequired2つの記述を行う必要がありません。


  props: {
    name: {
      type: String,
      default: "バッグ",
      required: true,
    },
  },

defaultが設定されているのでrequiredのtrueは必要ありません。

Validationを設定する(カスタムバリデーター)

type, default, requiredまでの設定については入門者の人でも理解することは簡単です。しかしValidationなどで関数の話が出てくると敬遠する人も多いと思いますが頑張って理解を深めていきましょう。

Validation(バリデーション)は入力した値が正しいかどうかチェックを行うことです。例えばパスワードの文字列が8文字以上が必須となっている場合は入力した文字列が8文字以上かどうかチェックを行うことをValidationを行うといいます。

例えば今回の例ではバッグに関する商品だけこのProductコンポーネントを利用できるとします。決められた名前以外の商品が入った場合は警告が出るようにValidationを利用します。

下記ではpropsで受け取ったnameの値がバッグ、トートバッグ、バックパックのいずれかであるかチェックを行っています。indexOfメソッドで配列の中にvalueの値が含まれているかチェックをしています。含まれていない場合は-1が戻り値として戻されます。バッグ、トートバッグ、バックパックの3つ以外の文字列が入ってくるとvalidationに失敗したことになります。


  props: {
    name: {
      type: String,
      default: "バッグ",
      validator: (value) => {
        return ["バッグ", "トートバッグ", "バックパック"].indexOf(value) !== -1;
      },
    },
  },

コンポーネントの値を与えるproductNamをトートバッグからショルダーバッグに変更を行います。


data() {
  return {
    productName: "ショルダーバッグ",
  };
},

ショルダーバッグはvalidatorで指定した配列の要素に含まれていないのでコンソールログのメッセージにはカスタムバリデーターが失敗している警告が表示されます。

validatorがまだ難しいと感じているかもしれないのでもう少し詳細に説明するとvalidatorではreturnでtrueかfalseを返すことで成功か失敗かが決められるので、return trueと記述すればいつも成功ということになります。return falseに変更すると必ずコンソールログに警告が表示されます。


  props: {
    name: {
      type: String,
      default: "バッグ",
      validator: () => {
        return true; 
      },
    },
  },
validationの関数に引数を入れていないのはvalueを入れると関数の中でvalueを使用していないためエラーが表示されるためです。通常はnameで設定した値がこのvalueに入るで必ずvalueを記述します。

Propsで受け取った値の文字列の長さでValidationを行いたい場合は下記のように記述することでvalidationを設定することができます。このValidataionでは6文字よりも多い文字数のものであればtrueになるため、3文字のバッグを入力した場合はfalseになり警告が表示されます。


  props: {
    name: {
      type: String,
      default: "ショッピングバッグ",
      validator: (value) => {
        return value.length > 6;
      },
    },
  },

アロー関数を使わない場合は下記のように記述することができます。


  props: {
    name: {
      type: String,
      default: "ショッピングバッグ",
      validator: function (value) {
        return value.length > 6;
      },
    },

Propsの値の更新(注意)

Propsは親コンポーネントから子コンポーネントへ一方的にpropsを渡すことができます。そのため子コンポーネントでは受け取ったpropsを更新してはいけません。子コンポーネントから親コンポーネントへデータを送りたい場合はeventを利用します。

Propsの次はSlotについて

親コンポーネントから子コンポーネントに値を渡すということではSlotも存在します。Propsが文字列、数値、配列やオブジェクトを渡すのに対してSlotではHTMLのタグを渡すことができます。今回Propsの理解を深めることができたのでPropsの次はSlotの理解を深めて行ってください。