Vue.jsでのコンポーネント間データ受け渡しについて勉強してみた

n.yuumi
n.yuumi

Vue.jsとは

Vue.jsは、コンポーネント指向の、ユーザーインターフェースを構築することに特化したJavaScriptのフレームワークです。
 

Vue.jsを勉強し始めた

Vue.jsの勉強を進めていく中で、最初の難関はコンポーネント間のデータ受け渡し処理でした。データの受け渡し方法はいくつかありますが、今回はpropsとemitに絞って親子コンポーネント同士でどのようにデータの受け渡しが行われているのかを説明していきたいと思います。
 

親子コンポーネント間のデータ受け渡し

データの受け渡しがどのように行われているのか確認するために、4つのコンポーネントを使って確認していきたいと思います。
今回は、テキストを入力し、追加ボタンでその入力されたテキストを随時一覧に表示していくという機能を作成していきます。
 
・親コンポーネント(App.vue)
→3つの子コンポーネントをインポート
 
・子コンポーネント(FirstChild.vue:黄緑)
→親コンポーネントから送信されたテキストを子コンポーネントで表示
 
・子コンポーネント(SecondChild.vue:桃色)
→入力されたテキストを追加ボタンで親コンポーネントに送信
 
・子コンポーネント(ThirdChild.vue:黄色)
→子コンポーネントから送信されたテキスト一覧を表示
 
Screen Shot 2021-03-02 at 16.59.01.png
 
Screen Shot 2021-03-02 at 16.58.53.png
 
 
App.vue(親)
<template>
  <div class="top">
    <first-child :hoge="title"></first-child>
    <second-child @input-text="addText"></second-child>
    <third-child :fuga="textList"></third-child>
  </div>
</template>
<script>
import FirstChild from './components/FirstChild.vue';
import SecondChild from './components/SecondChild.vue';
import ThirdChild from './components/ThirdChild.vue';
export default {
  components: {
    FirstChild,SecondChild,ThirdChild
  },
  data() {
    return {
      title: 'ボタンを押してテキストを一覧に追加',
      textList: [],
    }
  },
  methods: {
    addText(value) {
      const newText =  {
          id: new Date().toISOString(),
          text: value,
      };
      this.textList.unshift(newText);
    }
  }
}
</script>
FirstChild.vue(子)
<template>
  <p>{{ hoge }}</p>
</template>
<script>
export default {
  props: ['hoge'],
};
</script>
SecondChild.vue(子)
<template>
  <form @submit.prevent="addText">
    <div>
      <label for="test">テキストを入力</label>
      <input type="text" name="test" id="test" v-model="inputText"/>
    </div>
    <button @click="addInputText">一覧にテキスト追加</button>
  </form>
</template>
<script>
export default {
  data() {
    return {
      inputText: null,
    }
  },
  methods: {
    addInputText() {
      this.$emit('input-text',this.inputText);
      this.inputText = '';
    }
  }
};
</script>
ThirdChild.vue(子)
<template>
  <ul>
    <li v-for="item in list" :key="item">{{ item }}</li>
  </ul>
</template>
<script>
export default {
  props: ['fuga'],
  data() {
    return {
      list: this.fuga,
    }
  }
};
</script>
まず、App.vueとFirstChild.vueのデータ受け渡しについて説明していきます。
 
ここでは、親コンポーネントから送信されたデータを子コンポーネント側で取得するという処理を行っています。
 
1. 子コンポーネントをインポート
FirstChild.vueをインポートし(App.vue9行目)、をcomponentsプロパティに登録(App.vue14行目)することで親コンポーネント内で子コンポーネントへのアクセスが可能になります。
 
2. 子コンポーネントへ送信するデータを定義
受け渡したいデータをdataプロパティで定義します。titleというキーに対して「ボタンを押してテキストを一覧に追加」という値を設定しています。
 
3.データを子コンポーネントタグにバインド
v-bindディレクティブ機能を使うことでデータ送信が可能になります。これは、データを渡すための窓口のような役割を担っています。書き方は2種類あり、v-bind:プロパティ名="値"、もしくは省略記法の:プロパティ名="値"で書くことができますが、今回は省略記法を使っていきます。ここでは、hogeというプロパティに対して#2で定義したtitleを設定しています。
 
4.子コンポーネントでデータを受け取る
親から送信されたデータを子コンポーネント側で受け取る為には、propsプロパティを使います。
記述方法はprops: ['プロパティ名']です。今回は、親コンポーネントでhogeプロパティが指定されているので、子コンポーネントでprops: ['hoge']と指定します。
 
5.プロパティに設定されている値を表示
二重の波括弧でプロパティを囲ってあげると、#4で取得したhogeプロパティの値をhtmlタグ内で表示させることができます。
 
Screen Shot 2021-03-02 at 16.57.25.png
 
 
次に、App.vueとSecondChild.vueのデータ受け渡しについて説明していきます。
 
ここでは、子コンポーネントから親コンポーネントへデータ送信を行います。親→子へのデータ受け渡しの場合、子コンポーネント側でpropsを使いデータを受け取りますが、子→親へデータを受け渡す場合、emitを使います。
 
1. 子コンポーネントをインポート
SecondChild.vueをインポートし(App.vue10行目)、をcomponentsプロパティに登録(App.vue14行目)することで親コンポーネント内で子コンポーネントへのアクセスが可能になります。
 
2.送信するデータをセット
先ほどは、v-bindディレクティブを使い送信データをバインドさせましたが、今回はv-modelディレクティブを使用します。v-modelディレクティブは、フォームの値とdataプロパティの変数を連動させることができます。つまり、inputタグにv-model="inputText"と指定することで、inputタグに入力された値がリアルタイムでinputText(SecondChild.vue 20行目)に代入さレルようになります。
 
3. クリックイベント設定
追加ボタンを押したタイミングで、親コンポーネントにデータ送信されるように設定します。
ここで登場するのがv-onディレクティブです。これは、イベントリスナーをバインドすることができます。
v-onディレクティブもv-bindと同様に省略記法が存在します。通常は、v-on:イベント="Javascript処理名"のように書きますが、@イベント="Javascript処理名”のように省略して記述することが可能です。
ボタンタグに@click="addInputText"と指定し(SecondChild.vue 11行目)、クリック時にmethodsプロパティに定義したaddInputText()が実行されるようにします。
 
4.子コンポーネントからデータを送信
先述の通り、データ送信にはemitを使用します。
this.$emit('イベント名', 値);のように記述しますが、1つ目のパラメータにはイベント名、2つ目のパラメータにイベントハンドラに渡される値をそれぞれ指定してください。イベント名は任意で指定が可能ですので、今回は'input-text'と指定しました。また、引き渡す値は、入力データが代入されているthis.inputTextを設定しています。
 
5.親コンポーネントでデータ受信
子コンポーネントから送信されたinput-textイベント発火の通知は、v-onディレクティブで行います(App.vue 4行目)。
 
以上の設定により、子コンポーネントから親コンポーネントへのデータ受け渡し処理が完了しました。
 
Screen Shot 2021-03-02 at 16.57.07.png
 
 
最後に、入力されたデータを一覧に表示させていきます。
 
1. 子コンポーネントをインポート
ThirdChild.vueをインポートし(App.vue11行目)、をcomponentsプロパティに登録(App.vue14行目)することで親コンポーネント内で子コンポーネントへのアクセスが可能になります。
 
2. 入力データをdataプロパティの変数に格納
入力値を一覧にして表示させるため、配列格納用の変数textListを作成します(App.vue 19行目)。
input-textイベント発火のタイミングでtextListに入力データが格納されます(App.vue 23~29行目)。
 
3. 親コンポーネントから子コンポーネントにデータを送信
最初のデータ送信時に使用したv-bindディレクティブをここでも活用します。先ほどはhogeプロパティを指定しましたが、ここではfugaプロパティにtextListを設定します(App.vue 4行目)。
 
4.親コンポーネントからのデータを子コンポーネントで受信
子コンポーネントのpropsでfugaプロパティを指定し、入力データを取得します(ThirdChild.vue 4行目)。
 
5. 一覧データを表示
配列の場合は表示させるには、v-forディレクティブを使います(ThirdChild.vue 3~5行目)。
下記のように指定することでリストデータをレンダリングさせることが可能です。
v-for="配列の要素1つ in 配列" :key="一意の値"
今回は、keyに一意のデータを設定する為、暫定的に日付を設定してあります。
 
Screen Shot 2021-03-02 at 16.56.41.png
 

最後に

Vue.jsの勉強を始めた時は、データの流れがどのように行われているか理解するのが大変でした。今回は4つのコンポーネントでしたが、もっと多くのコンポーネントが存在する場合、このデータの流れを把握できていないと悲惨なことになります。Vue.jsには、props、emit以外にもprovideやinjectでデータを受け渡す方法が存在するので、そちらの理解も深めていきたいと思います。