本章では、データに格納した価格の数値は変更せず、関数を使って価格をカンマ付きの表示にするようプログラムを改修していきます。 本章を実践すると、関数を使ってデータを操作・加工することができるようになります。
今度は、商品価格の表示を変更してみましょう。価格の表示は、例えば 1,180円
のように 3 桁ごとにカンマを入れた表示が一般的です。しかし、items
の price
にはカンマのない価格が格納されています。
このようなとき、price
に格納する値を数値から文字列に変更し、カンマを追加するのも 1 つの手です。しかし価格のデータを文字列にしてしまうと、今度は足したり引いたりといった数値計算ができなくなってしまいます。
そこで、items
の中身は変更せず、データを加工して表示のみを変更してみましょう。
現在の <template>
内のコードは以下のようになっています。
<<< @/../examples/v-if/src/App.vue#template
価格の表示を行なっている部分は、下記のコード部分です。
<span>¥<span class="price">{{ item.price }}</span></span>
この部分のコードを改修して、item.price
を 3 桁ごとにカンマを入れた表示に変えてみましょう。
::: v-pre
{{ }}
の中には、値だけではなく処理を記述できます。
:::
<span>¥<span class="price">{{ item.price.toLocaleString() }}</span></span>
JavaScript がもともと持っている toLocaleString()
関数を用いて、3 桁ごとにカンマを入れた表示にできました。
::: v-pre
このように、{{ }}
の中に直接実行したい処理を記述できますが、処理が長くなると可読性が下がってしまいます。そこで、実行したい処理を関数にして登録し、{{ }}
にはその関数を記述するとよりスマートです。
:::
通常の JavaScript のように関数宣言で定義することで、関数を追加することができます。
item.price
を引数として渡し、toLocaleString()
の結果を返す関数を作成してみましょう。
<script setup>
内に関数を定義していきます。
まず、引数を 3 桁ごとにカンマを入れた値として返す関数として、pricePrefix()
という関数を定義してみましょう。以下のハイライトしている箇所(pricePrefix()
以下)を追加します。
<<< @/../examples/methods/src/App.vue#script{43-49}
::: v-pre
pricePrefix()
が定義できたら、実際に {{ }}
の中で使用してみましょう。
:::
<span>¥<span class="price">{{ pricePrefix(item.price) }}</span></span>
関数を使って、価格のデータはそのままで、表示を加工できました。また、他の箇所で同じ処理を行いたい場合に使い回すこともできるようになりました。このように、Vue.js でデータの操作や処理を行う場合は関数を使用するようにしましょう。
Vue.js には関数の他に、算出プロパティがあります。算出プロパティは computed
を使って関数と同様に定義します。以降、関数との違いについて説明していきます。
まず、関数と算出プロパティの違いを知るために、template
でどのように扱うのかをそれぞれ確認してみましょう。
確認の準備として、商品数を表示する関数を定義していきます。
<template>
<header class="header">
<img src="/images/logo.svg" alt="" />
<h1>Vue.js ハンズオン</h1>
</header>
<div>商品数:{{ stockQuantity() }}</div>
<!-- 省略 -->
<template>
<script setup>
// 省略
/**
* 価格を3桁ごとのカンマ付きで返す
* @param {number} price 価格
*/
function pricePrefix(price) {
return price.toLocaleString()
}
/**
* 在庫のある商品数を返す
*/
function stockQuantity() {
return items.value.filter(item => item.soldOut === false ).length
}
</script>
新たな関数として stockQuantity()
を定義し、template
内で使用しています。stockQuantity()
では filter()
関数によって在庫がある商品を抽出し、length
プロパティによって抽出した数を取得しています。
ここにv-else や v-else-if の使い方のコードを参考に、入荷のボタンを追加してみます。
<div v-if="!item.soldOut">
<!-- 省略 -->
</div>
<div v-else>
売り切れです<button type="button" @click="stockItem(item)">入荷</button>
</div>
<script setup>
// 省略
/**
* 在庫のある商品数を返す
*/
function stockQuantity() {
return items.value.filter(item => item.soldOut === false ).length
}
/**
* 商品の在庫状況を変更する
* @param {object} 商品情報
*/
function stockItem(item) {
item.soldOut = false
}
</script>
入荷ボタンをクリックすると stockItem()
によって items
配列の中身が変更されます。items
配列が変更されたことにより template
が更新されるので、stockQuantity()
が実行され、商品数が 3 から 4 に更新されます。
関数で商品数を表示しましたが、算出プロパティでも computed
を使って商品数を表示することができます。
<script setup>
import { ref, computed } from 'vue'
// 省略
const stockQuantityComputed = computed(function() {
return items.value.filter(item => item.soldOut === false ).length
})
</script>
処理の内容は 関数で商品数を取得した stockQuantity()
と同じです。それでは作成した stockQuantityComputed
を template
で使用してみましょう。
<template>
<header class="header">
<img src="/images/logo.svg" alt="" />
<h1>Vue.js ハンズオン</h1>
</header>
<div>商品数:{{ stockQuantityComputed }}</div>
<!-- 省略 -->
</template>
商品数には 3 が表示され、入荷ボタンをクリックすると 4 に更新されます。
::: tip ヒント
関数で定義したものは JavaScript のまさに関数ですが、computed
を使うことで、JavaScript のオブジェクトで定義したプロパティと同じように扱うことができる算出プロパティとして定義されます。そのため、computed
で定義された算出プロパティを template
で使う際には ()
は不要です。
:::
関数でも算出プロパティでも最終的に同じ値を取得することが可能ですが、2 つの違いはどこにあるのでしょうか。
大きな違いとして 算出プロパティにはキャッシュ機能があるため、値の更新タイミングが異なります。
- 関数 :
template
が更新された時 - 算出プロパティ :
items
が更新された時
実際に値の更新タイミングを確認するために、現在時刻を表示する処理を追加していきます。
<!-- 省略 -->
<div>商品数:{{ stockQuantity() }}</div>
<div>現在時刻:{{ getDate() }}</div>
<script setup>
// 省略
function stockItem(item) {
item.soldOut = false
}
/**
* 現在時刻を取得する
*/
function getDate() {
return Date.now()
}
</script>
入荷ボタンをクリックすると商品数だけでなく、現在時刻も更新されます。関数を更新すると template
の更新処理が実行されるため、定義されている他の関数も更新されるからです。
次に算出プロパティに現在時刻を取得する処理を追加してみましょう。
<!-- 省略 -->
<div>現在時刻:{{ getDate() }}</div>
<div>現在時刻(computed):{{ getDateComputed }}</div>
<script setup>
// 省略
const getDateComputed = computed(function() {
return Date.now()
})
</script>
関数で定義されていた getDate()
では、入荷ボタンをクリックすると更新されましたが、computed
を使って定義された算出プロパティ getDateComputed
は更新されません。この算出プロパティの値の更新タイミングは items
が更新された場合であり、items
を扱っていないと自動的に値が更新されないことが理由です。
以上のように、関数と算出プロパティでは更新のタイミングが違うので注意が必要です。
::: tip ヒント
毎回自動的に更新がされるため、関数の方が便利そうです。しかし、これには注意が必要です。例えば、巨大な配列をループして多くの計算が必要な処理があったとします。関数で定義した場合 template
が更新される度に、このループ処理が実行されてしまいます。しかし、算出プロパティで定義していた場合はキャッシュが効いているので実行されません。
:::