【サンプル付】Vue.jsでComponentを作成する! 正しくpropsを使おう! - inokawablog

【サンプル付】Vue.jsでComponentを作成する! 正しくpropsを使おう!

vue.jsは自由度が高いので、型などを意識せずともそれなりのものを作ることができます。しかし、エラーや保守性を考えた場合、型定義や必須項目の設定をしておかないと後々面倒になるのでしっかりと理解しておきましょう!

vue.jsでコンポーネントを作成する時に注意するポイントがあります。

  1. 型の安全性
  2. 必須項目の設定
  3. デフォルトの設定

それぞれを見ていきます。

型の安全性

変数名によっては解釈の余地があるため型にばらつきが出てしまうかもしれません。そのために型の安全性を高めるためにプロパティの型を指定してあげる必要があります。そのため、作る型を予測して定義しましょう。

予測される型の種類は以下のそれぞれのいずれかになります。また、typeはカスタムコンストラクタ関数でもあります。

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

以下のようにして指定する

<script>
export default {
    props: {
        length: {
            type: Number,
        }
    }
}
</script>

また、複数のデータ型を定義することも可能です。

<script>
export default {
    props: {
        length: {
            type: [Number, String],
        }
    }
}
</script>

必須項目の設定

required propertyを使うことで必須項目に設定することができます

<script>
export default {
    props: {
        length: {
            type: [Number, String],
            required: true
        }
    }
}
</script>

デフォルトの設定

defaultを設定することで値が渡されなかった場合でも初期値としてDefaultの値を使用することができるます。

<script>
export default {
    props: {
        length: {
            type: Number,
            required: true,
            default: 90
        }
    }
}
</script>

TypeをObjectまたはArrayに指定する場合、デフォルトは必ずそれを生み出すための関数を返す必要があります。

<script>
export default {
    props: {
        length: {
            type: Object,
            default: function () {
                    return { message: 'hello' }
            }
        }
    }
}
</script>

また、型の定義がされている場合は、requiredを追加する必要はありません。

<script>
export default {
    props: {
        length: {
            type: Number,
            default: 90
        }
    }
}
</script>

propsにはバリデーションを設定することもできます。

<script>
export default {
    props: {
        data: {
                validator: function (value) {
                // プロパティの値は、必ずいずれかの文字列でなければならない
                return ['success', 'warning', 'danger'].indexOf(value) !== -1
             }
         }
     }
}
</script>
そしてこれらはあくまで開発用の機能だということ。
なので、値を渡した時に型が違うからといって、動かなくなるわけではなく、コンソールエラーがでるだけになります。

サンプル

パン屋さんの商品に関してカードコンポーネントを作成して見ます。

構成要素としては、以下になると思います。

  • 商品画像
  • 値段
  • 商品名
  • 商品説明
  • 期間限定

画像は必須ではなく、もし、画像がない場合は、デフォルトでno imageの画像を表示するようにしましょう。
商品名は必須ですね。しかし、デフォルトでは名前を設定することはできません。
商品説明は必須ではありませんが、文字制限があったほうがいいですね。
値段は、一定の値段が決まっている場合は必要ですね。パンの基本料金が100円とします。
期間限定で発売される商品もあると思います。この場合、日付を指定してあげます。

これらをまとめると以下のようになると思います。

オプション名 オプション 必須 デフォルト
商品画像 productImage String - no image画像アドレス
商品名 productName String -
商品説明 productDescription String - -
値段 productPrice Number 100
期間限定 limitedEditionDate Date - -

Codepenで作成しました。Codepenではエラーは確認できませんが、Jsfiddleで確認することができます!(※Jsfiddleでは作っていません)

template

<div id="example">
  <card-component :product-image="'https://d1f5hsy4d47upe.cloudfront.net/31/3117b0d5a507a0f5a36f60114ffa8148_t.jpeg'" :product-name="'クロワッサン'" :product-price="180" :product-description="'美味しい美味しいクロワッサン'"></card-component>
  <card-component :product-image="" :product-name="'食パン'" :product-description="'とっても美味しい食パン'"></card-component>
  <card-component :product-image="" :product-name="'期間限定特別パン'" :product-description="'期間限定パン'" :limited-edition-date="new Date()"></card-component>
</div>
var CardComponent = Vue.extend({
  props: {
    productImage: {
      type: String,
      default: 'https://www.d-elf.com/wp-content/themes/dp-fancie-note-business/img/post_thumbnail/noimage.png'
    },
    productName: {
      type: String,
      required: true
    },
    productDescription: {
      type: String,
      validator: function (value) {
        return value.length >= 2
      }
    },
    productPrice: {
      type: Number,
      default: 100
    },
    limitedEditionDate: {
      type: Date
    }
    },
  template: '{{products}}<section class="card"><img class="card-img" :src="productImage"><div class="card-content"><h1 class="card-title">{{productName}}</h1><p class="limited-edition-date" v-if="limitedEditionDate">期間限定:{{limitedEditionDate | dateFormatter}}まで!!</p><p class="card-text">{{productDescription}}</p></div><div class="card-link"><div>{{productPrice}}円</div></div></section>',
  filters: {
    dateFormatter: function (date) {
      return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate()
    }
  }
})

Vue.component('card-component', CardComponent)
new Vue({ el: '#example' })
.card {
  width: 350px;
  background: #fff;
  border-radius: 5px;
  box-shadow: 0 2px 5px #ccc;
}
.card-img {
  border-radius: 5px 5px 0 0;
  width: 100%;
  object-fit: cover;
  max-width: 100%;
  max-height:200px;
  height: auto;
}
.card-content {
  padding: 20px;
}
.card-title {
  font-size: 20px;
  margin-bottom: 20px;
  text-align: center;
  color: #333;
}
.card-text {
  color: #777;
  font-size: 14px;
  line-height: 1.5;
}
.card-link {
  text-align: center;
  border-top: 1px solid #eee;
  padding: 20px;
}
.card-link a {
  text-decoration: none;
  color: #0bd;
  margin: 0 10px;
}
.card-link a:hover {
  color: #0090aa;
}

.limited-edition-date{
  font-size:12px;
  color: red;
}

これで確認することがきます!!

良いコンポーネントを作成しましょう!!