VueJS 速習チュートリアル

Vue Provide / Inject

Vue の Provide / Inject は、あるコンポーネントから他のコンポーネントへデータを配信するために使用されます。これは特にコンポーネント階層が深くなる大規模なプロジェクトで非常に有用です。

  • Provide(プロバイド): データを他のコンポーネントから利用可能な状態(公開)にします。
  • Inject(インジェクト): 公開されたデータを受け取って使用します。

Provide / Inject は、コンポーネント間でデータを共有する際、props(プロップス)を使用する代わりの強力な手段となります。

1. Provide / Inject の仕組み

コンポーネントの中にさらにコンポーネントが入っているような大規模プロジェクトでは、App.vue から深い階層にあるサブコンポーネントへ props でデータを渡すのは困難です。なぜなら、データが通過するすべての中間コンポーネントprops を定義しなければならないからです(これを「プロップ・ドリリング」や「バケツリレー」と呼びます)。

props の代わりに Provide / Inject を使用すれば、データを「提供(Provide)」する場所と、データを「注入(Inject)」する場所だけで定義を完結させることができます。

2. データの提供 (Provide)

データを他のコンポーネントから利用可能にするには、provide オプションを使用します。

2.1 実装例 (App.vue)

以下のコードでは、foods 配列を provide することで、プロジェクト内の他のどのコンポーネントからでもインジェクト(注入)して利用できる状態にしています。

App.vue:

<template>
  <h1>食べ物リスト</h1>
  <div @click="this.activeComp = 'food-about'" class="divBtn">概要</div>
  <div @click="this.activeComp = 'food-kinds'" class="divBtn">種類</div>
  <div id="divComp">
    <component :is="activeComp"></component>
  </div>
</template>

<script>
export default {
  data() {
    return {
      activeComp: 'food-about',
      // このデータを配信したい
      foods: [
        { name: 'ピザ', imgUrl: '/img_pizza.svg' },
        { name: 'リンゴ', imgUrl: '/img_apple.svg' },
        { name: 'ケーキ', imgUrl: '/img_cake.svg' },
        { name: '魚', imgUrl: '/img_fish.svg' },
        { name: 'ご飯', imgUrl: '/img_rice.svg' }
      ]
    }
  },
  provide() {
    return {
      // foods 配列を外部に公開
      foods: this.foods
    }
  }
}
</script>

3. データの注入 (Inject)

App.vuefoods 配列が provide されたので、これを FoodKinds コンポーネントで受け取ることができます。

FoodKinds コンポーネントに foods データをインジェクトすることで、App.vue にある元のデータを使用して、さまざまな食べ物を表示することが可能になります。

3.1 実装例 (FoodKinds.vue)

FoodKinds.vue:

<template>
    <h2>さまざまな食べ物の種類</h2>
    <p><mark>このアプリケーションでは、食べ物データは "App.vue" で提供(Provide)され、この "FoodKinds.vue" コンポーネントで注入(Inject)されて表示されています:</mark></p>
    <div v-for="x in foods" :key="x.name">
        <img :src="x.imgUrl">
        <p class="pName">{{ x.name }}</p>
    </div>
</template>

<script>
export default {
    // 公開されている 'foods' を取得
    inject: ['foods']
}
</script>

<style scoped>
    div {
        margin: 10px;
        padding: 10px;
        display: inline-block;
        width: 80px;
        background-color: #28e49f47;
        border-radius: 10px;
    }
    .pName {
        text-align: center;
    }
    img {
        width: 100%;
    }
</style>