基礎からメモ: Vue.js CH4-p120 算出プロパティの使い方
算出プロパティ
算出プロパティはcomputed
プロパティに定義した関数で任意のデータを処理するもの。
width
プロパティの値の半分を割り出して、その値を返すhalfWidth
関数を算出プロパティとして定義。
var app = new Vue({ el: '#app', data: { width: 800 }, computed: { halfWidth: function() { return this.width / 2; } } })
HTML側からは算出プロパティに定義した関数名を、その関数が返す値として使えるので、あたかもhalfWidth
というプロパティがあるかのように見える。これでテンプレートに複雑な式を書かなくてすむ。
<div id="app"> <p>{{ width }} の半分は {{ halfWidth }}</p> </div>
算出プロパティを組合せる
算出プロパティとしてインスタンスのcomputed
オプションに定義した別の関数halfWidth
とhalfHeight
を使ったもう一つの算出プロパティhalfPoint
で、中心ポイントを表すオブジェクトを返す。
<div id="app"> <p>X: {{ halfPoint.x}}</p> <p>Y: {{ halfPoint.y}}</p> </div>
var app = new Vue({ el: '#app', data: { width: 800, height: 600 }, computed: { halfWidth: function() { return this.width / 2; }, halfHeight: function() { return this.height / 2; }, // width x height の中心座標 halfPoint: function() { return { x: this.halfWidth, y: this.halfHeight } } } })
結果表示
<div id="app"> <p>X: 400</p> <p>Y: 300</p> </div>
ゲッターとセッター
算出プロパティにゲッターやセッターを定義することができる。両方定義することで、データを操作するとどちらからでも同期できる(双方向フロー)。
ゲッターはget
、セッターはset
という名前の関数を、算出プロパティの関数内にオブジェクトとして定義するだけである。
ゲッターに紐付けたinputに入力すれば、データのwidth
を変更でき、同時にその半分のデータも変わる。半分のデータを入力すればwidth
データもその倍になる。
<div id="app"> <input v-model.number="width"> {{ width }} <input v-model.number="halfWidth"> {{ halfWidth }} <p>{{ width}}</p> <p>{{ halfWidth}}</p> </div>
var app = new Vue({ el: '#app', data: { width: 800 }, computed: { halfWidth: { get: function() { return this.width / 2 }, set: function(val) { this.width = val * 2 } }, } });
算出プロパティのキャッシュ機能
算出プロパティはリアクティブなデータにのみ依存してキャッシュを構築、再構築する。算出プロパティ内で使われるデータがリアクティブではない場合、再構築されない。それに対して、メソッドが返すデータはその都度実行されるため、呼び出されるたびに再構築される。
次のように算出プロパティとメソッドの両方に乱数を返す操作が2箇所づつある場合、メソッドの返す数値は描画のたびに2箇所の数値が違ったものになるが、算出プロパティが返す乱数はリアクティブデータではないため2箇所とも同じ数値が返る。
<div id="app"> <!-- 算出プロパティ --> <ol> <li>{{ computedData }}</li> <li>{{ computedData }}</li> </ol> <!-- メソッド --> <ol> <li>{{ methodsData() }}</li> <li>{{ methodsData() }}</li> </ol> </div>
var app = new Vue({ el: '#app', computed: { computedData: function() { return Math.random(); } }, methods: { methodsData: function() { return Math.random(); } } });
リストの絞り込み
算出プロパティのキャッシュ機能によって、複雑な処理内で更新の無いデータを再利用し負荷を軽減できる。
リストから「◯円以下」の商品を「◯件」表示させる例。limit
の数値を変更してもbudget
が変わらなければmatched
の計算結果が使われる。
<div id="app"> <input v-model.number="budget"> 円以下に絞り込む <input v-model.number="limit"> 件を表示 <p>{{ matched.length }} 件中 {{ limited.length }} 件を表示中 <ul> <!-- v-forでは最終結果、算出プロパティのlimitedを使用する --> <li v-for="item in limited" v-bind:key="item.id"> {{ item.name }} {{ item.price }}円 </li> </ul> </div>
var app = new Vue({ el: '#app', data: { budget: 500, limit: 5, list: [ { id: 1, name: 'りんご', price: 100 }, { id: 2, name: 'ばなな', price: 200 }, { id: 3, name: 'いちご', price: 400 }, { id: 4, name: 'オレンジ', price: 300 }, { id: 5, name: 'メロン', price: 500 }, ] }, computed: { matched: function() { return this.list.filter(function(el) { return el.price <= this.budget; }, this) }, limited: function() { return this.matched.slice(0, this.limit); } } });
ソート機能の実装
javascriptのsort
は配列を直接操作する。そのため算出プロパティ内で使用すると元のデータが変わってしまう。それを防ぐためにシャローコピーを使用したりLodash
などのライブラリを使う方法がある。
this.list.slice(0).sort();
先のリスト絞り込みにソート機能のあるボタンを追加してみる。
<body> <div id="app"> <input v-model.number="budget"> 円以下に絞り込む <input v-model.number="limit"> 件を表示 <p>{{ matched.length }} 件中 {{ limited.length }} 件を表示中 <ul> <!-- v-forでは最終結果、算出プロパティのlimitedを使用する --> <li v-for="item in limited" v-bind:key="item.id"> {{ item.name }} {{ item.price }}円 </li> </ul> <button v-on:click="order=!order">切り替え</button> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.5/lodash.min.js"></script> <script src="main.js"></script> </body>
matched
とlimited
の間にソート処理関数sorted
を定義する。
[main.js]
var app = new Vue({ el: '#app', data: { budget: 500, limit: 5, list: [ { id: 1, name: 'りんご', price: 100 }, { id: 2, name: 'ばなな', price: 200 }, { id: 3, name: 'いちご', price: 400 }, { id: 4, name: 'オレンジ', price: 300 }, { id: 5, name: 'メロン', price: 500 }, ], order: false }, computed: { matched: function() { return this.list.filter(function(el) { return el.price <= this.budget; }, this) }, sorted: function() { return _.orderBy(this.matched, 'price', this.order ? 'desc' : 'asc'); }, limited: function() { // limited で使うデータを matched から sorted に変更 return this.sorted.slice(0, this.limit); } }, });
切り替えボタンはorder
プロパティを逆転させているだけだが、リアクティブデータのorder
が変化するので、sorted
とlimited
の算出が行われる。