算出プロパティについて
vue.jsの 「算出プロパティ」 について調べてみると、
- 関数で算出したデータを返すことができるプロパティのこと
- 任意に処理を含めることのできるデータ
- 算出プロパティは、Vueインスタンスのcomputedというプロパティのことで、テンプレート側に処理した値を渡すことができる…
もう何を言っているのかよくわからないですよね…。
なので、ここでは「算出プロパティ」について、
調べたらよく出てくるフレーズや説明を実際に例を見ながら、できるだけ簡単に変換して見ていくことにしましょう。
といっても、なかなか難しくて理解しにくい内容でもあるので、
わからなくてもどんどん先に読み進めていっても大丈夫なように作ってあります。
最終的に 「算出プロパティを扱える(書ける)ようになる」 ところを目指しているので、
ちょっと頑張って最後までたたどり着いてください。
算出プロパティ:データそのものに何らかの処理を与えたもの
まず、「データそのものに処理を与えたもの」というよく出てくる表現ですね。
「データそのもの」のデータと言うのは、次の例で言うと 「fullName」 のことです。
<div id="app">
苗字:<input v-model="lastName"><br>
名前:<input v-model="firstName">
<p>氏名: {{ fullName }}</p>
</div>
一見 「fullName
がdataに用意されていて、その中の文字列が表示される」
と、いったような記述に見えます。
しかし、実は少し違います。
次の例見てください。
lastName
と firstName
に入力された瞬間に fullName
に自動で結合された文字が入力されています。
ここで重要なのは、もともとdataに設定されていた文字列ではなく、
「今、入力された文字列が fullName
に入った」 ということです。
言い換えると、 これはfullName
という値(プロパティ)に「文字列を結合する処理が付いている」という事です。
さらに言い換えると、 「文字列を結合するという能力を持ったfullName
という値(プロパティ)」が使われているのです。
それでは、書き方です。
JavaScriptには次のように記述してください。
var vm = new Vue({
el: '#app',
data: {
lastName: '',
firstName: ''
},
computed: {
fullName: function () {
return this.lastName + ' ' + this.firstName
}
}
})
computed
というのを使って、関数を作るのと同じように処理を書いていきます。
これだけで、氏名の欄に結合した結果が表示されるようになります。
メソッドと算出プロパティの違い
書き方も使い方も、一見普通の関数と同じに見えます。
しかし、大きな違いは HTML側で {{ fullName }}
と書いただけで、fullName
の処理を実行して、その結果を返すことができている点です。
通常の関数なら「クリックされたら」「submitされたら」など、何かアクションが起きてから実行されていたはずです。
もし、メソッドを使っていたとしたら {{ fullName() }}
のように ()
が必要になりますよね。
しかし、算出プロパティを使うと、fullNameと言うプロパティ自体が処理を持っているので、メソッドのように ()
を使って呼び出す必要がありません。
これが、
『データ(fullName)そのものに何らかの処理(苗字と名前を結合させ処理)を与えたもの』 とう意味です。
メソッド | 算出プロパティ |
---|---|
呼び出すときに () が必要 |
プロパティだけでOK |
キャッシュできない | キャッシュできる |
メソッドと算出プロパティの違いは、 ()
が必要かどうか、というよりも重要なことがあります。
このあたりから、内容が複雑になってくるのでちょっと注意してください。
算出プロパティはキャッシュができる…?
算出プロパティのキャッシュについて、公式ドキュメントには次のように書かれています。
「算出プロパティは、リアクティブな依存関係が更新されたときにだけ再評価され…」
早速よくわからなくなったので、例をみてみましょう。
<HTML>
<!DOCTYPE html>
<html lang="ja"">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="app">
メソッド
<ol>
<li>{{ getData() }}</li>
<li>{{ getData() }}</li>
<li>{{ getData() }}</li>
</ol>
算出プロパティ
<ol>
<li>{{ data }}</li>
<li>{{ data }}</li>
<li>{{ data }}</li>
</ol>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
<JavaScript>
var app = new Vue({
el: '#app',
computed: {
data: function() {
console.log("computed");
return Math.random();
}
},
methods: {
getData: function() {
console.log("method");
return Math.random();
}
}
});
乱数を発生させるだけの同じ処理を「メソッド」と「算出プロパティ」で比較したものです。
とりあえず全部コピーしたら、実行してブラウザを何度か更新してみてください。
すると、次のこと気づくと思います。
・メソッド側の処理は3つとも違う値
・算出プロパティの方は3つとも値が全て同じ
これが、キャッシュの有無による違いです。
メソッド
の場合は getData()
を3つ並べると3回分の呼び出しが別々に行われます。
つまり、3回の計算が実行されて、3回分のランダムな値が表示されています。
算出プロパティ
は一度計算されると、その結果が内部で保持されます。
2回目以降は、その保持された結果を表示することができるので、3つ並べたとしても 3回計算が行われるということはありません。
キャッシュについて詳しく
メソッドと算出プロパティに違いがあることは、なんとなくわかったと思います。
次は、もっと具体的に 「算出プロパティを使うべき理由」 を例を見ながら確認してみましょう。
まず、押されるたびに数字を増やしていくボタンを作成します。
まず、次のHTMLとmain.jsを書いてください。
<index.html>
<body>
<div id="app">
<p>{{ counter }}</p>
<button v-on:click = "counterBtn()">+1</button>
<!-- 「v-on」はよく使うので、以下のように書き換えることもできます。
<button @click="counterBtn">+1</button> -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
<main.js>
var app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
counterBtn: function() {
this.counter++;
}
}
});
では、数字によって「3以上」と「3以下」が表示されるような実装をしてみます。
次のように三項演算子を {{ }}
の中に入れて書くことができるので使ってみましょう。
<div id="app">
<p>{{ counter }}</p>
<button v-on:click = "counterBtn()">+1</button>
<p>{{ counter > 3 ? '3以上' : '3以下' }}</p>
</div>
このように、処理を直接書き込むことができるのですが、大規模な開発になってくるとコードが非常に読みにくくなってきます。
それを算出プロパティで書くとスッキリ見やすくなるという利点もあります。
三項演算子の処理を lessThanThree
というプロパティにまとめてみましょう。
<div id="app">
<p>{{ counter }}</p>
<button v-on:click = "counterBtn()">+1</button>
<p>{{ lessThanThree }}</p>
</div>
三項演算子の処理は main.js
側に移します。
算出プロパティとして書くので、computed
の中に書いてください。
var app = new Vue({
el: '#app',
data: {
counter: 0,
},
//追加分↓
computed: {
lessThanThree: function() {
return this.counter > 3 ? '3以上' : '3以下'
}
},
methods: {
counterBtn: function() {
this.counter++;
}
}
});
三項演算子では counter > 3 ? '3以上' : '3以下'
と書いていましたが、
vue.jsの中に書くときは、 counter
の先頭に this
を書くことを忘れずに注意してください。
データ内のプロパティにアクセスするためにはthisを使うのでしたね。
これで、算出プロパティの作成が完了しました。
メソッドとの挙動の違い
もちろん、メソッドを使っても同じことができます。
先ほどのコードに、同じ内容のメソッドを追加してみましょう。
<main.js>
var app = new Vue({
el: '#app',
data: {
counter: 0,
},
computed: {
lessThanThree: function() {
return this.counter > 3 ? '3以上' : '3以下'
}
},
methods: {
counterBtn: function() {
this.counter++;
},
//追加分↓(直前のカンマを忘れずに書いてください)
lessThanThreeMethod: function() {
return this.counter > 3 ? '3以上' : '3以下';
}
}
});
<index.html>
<div id="app">
<p>{{ counter }}</p>
<button v-on:click = "counterBtn()">+1</button>
<p>{{ lessThanThree }}(算出プロパティ)</p>
<p>{{ lessThanThreeMethod() }}(メソッド)</p>
</div>
<p>{{ lessThanThree }}</p>
のすぐ下に
<p>{{ lessThanThreeMethod() }}</p>
を追加しましょう。
こちらはメソッドなので()
が要りますね。
見た目は全く同じ動きをするものが完成しました。
内部的な動きを確認する
突然ですが、作成したこの lessThanThreeMethod()
が起動するタイミングはいつになると思いますか?
一見、ボタンが押されたらに見えますが、実はそれだけではありません。
lessThanThreeMethod()
が起動するタイミングは、
「画面が少しでも再描画されたとき」
この時にもメソッドは起動してしまいます。
つまり、lessThanThreeMethod()
とは 全く関係ない箇所 で、画面に少しでも変化があったとすると、
それだけで {{ }}
の中に書いたメソッドは動き出してしまいます。
それを確認するために、2つの処理とは 「関係ないボタン」 を作成してみましょう。
<div id="app">
<p>{{ counter }}</p>
<button v-on:click = "counterBtn()">+1</button>
<p>{{ lessThanThree }}(算出プロパティ)</p>
<p>{{ lessThanThreeMethod() }}(メソッド)</p>
<!-- 追加分 -->
<button v-on:click = "unrelatedBtn()">関係ないボタン</button>
<p>{{ unrelated }}</p>
</div>
var app = new Vue({
el: '#app',
data: {
counter: 0,
//追加分↓
unrelated: 0
},
computed: {
lessThanThree: function() {
console.log('算出プロパティのlessThanThree起動');
return this.counter > 3 ? '3以上' : '3以下'
}
},
methods: {
counterBtn: function() {
this.counter++;
},
lessThanThreeMethod: function() {
console.log('メソッドのlessThanThreeMethod起動');
return this.counter > 3 ? '3以上' : '3以下';
},
//追加分↓
unrelatedBtn: function() {
this.unrelated++;
}
}
});
dataに
unrelated: 0
を追加。
算出プロパティとしてunrelatedBtn
の処理を追加しました。
<今ある処理>
・算出プロパティの lessThanThree
:〇以下(算出プロパティ)
・メソッドの lessThanThreeMethod
:〇以下(メソッド)
・「+1ボタン」とその表示: unrelatedBtn
・「関係ないボタン」とその表示unrelatedBtn
【これから確認すること】
この 「関係ないボタン」を押すと、画面の一部が変わります(0 → 1)
つまり、 画面に少しでも変化あったら という状況ができてしまいました。
画面が再描画されたことで、{{ }}
の中にある lessThanThreeMethod()
が起動してしまっていることを今から確認します。
わかりやすく、処理の始まりに console.log
を入れます。
デベロッパーツールを開いて、 console
で処理を確認してみましょう。
「関係ないボタン」を押しただけで「メソッドのlessThanThreeMethod起動」が出力されているのがわかります。
一方、算出プロパティで記述した方はコンソールに何も出力されていません。つまり、処理は動いていないと言う事ですね。
これが、算出プロパティを使う最大の理由です。
この程度なら特に影響はないように見えますが、例えば大規模な開発で、
大量のデータが格納された配列を処理するメソッドが再描画ごとに動いていたら大変ですね。
Vue側で膨大な量のデータを加工するケースにおいてはメソッドではなく算出プロパティを使用することが重要になってきます。
算出プロパティを作成する練習
算出プロパティを実際に作成する時の考え方、書き方について練習してみましょう。
ネットショッピングを例にします。
商品を選択していくと、合計金額が 「選択した瞬間に計算され、表示される」 というのがあったとします。
これは算出プロパティを使いますね。
一方、例えば「確定ボタン」を押して初めて合計金額がわかるようなものは、関数で作成したりします。
リアルタイムで合計金額がわかるというのは、商品が選択される度に計算処理をするという事です。
画面が再描画される度(商品が選択される度)にメソッドが動いてしまうと負荷がかかりますね。
今から作るのは、話をもっと簡単にして、入力された2つの計算結果をリアルタイムで表示するというものを作成しながら、手順を確認していきます。
HTMLとJSを準備してください。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p>2つの商品の合計は〇〇円です</p>
商品1の値段:<input type="text">円<br>
商品2の値段:<input type="text">円
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="js/main.js"></script>
</body>
</html>
var app = new Vue({
el: "#app",
});
①dataに「item1」と「item2」を持たせます
var app = new Vue({
el: "#app",
data: {
item1: '',
item2: ''
},
});
初期値は空にしておきます。
②「item1」と「商品1の値段」を紐づけます
<div id="app">
<p>2つの商品の合計は〇〇円です</p>
商品1の値段:<input type="text">円<br>
商品2の値段:<input type="text">円
</div>
v-model
を使って「商品2の値段」も同じように紐づけましょう。
③算出プロパティに2つの数値を計算して返す処理を記述します
var app = new Vue({
el: "#app",
data: {
item1: '',
item2: ''
},
computed: {
total: function() {
return Number(this.item1) + Number(this.item2);
}
}
});
total
という名前で処理は「item1」と「item2」の合計です。dataの値にアクセスするのでthis
を使います。
そして、このままだと数字が文字列として認識されてしまうので、数値に変換するためにNumber
で囲っています。
④合計の結果を {{ }}
を使って表示させる
<div id="app">
<p>2つの商品の合計は {{ total }}円です</p>
商品1の値段:<input type="text" v-model="item1">円<br>
商品2の値段:<input type="text" v-model="item2">円
</div>
まとめ
今回は、算出プロパティの書き方について見てきました。
算出プロパティ
はcomputed
の中に書く
メソッド
はmethods
の中に書く
という事は覚えておきましょう。
どちらも中身の書き方自体は、普通のJavaScriptの関数を作成する時とほぼ同じです!