Vue.jsでは処理を行う方法にメソッド(method)やウォッチ(watch)以外に、算出プロパティ(computed)という方法があります。
メソッド(method)は他の言語でも多用するので直感的にわかりやすいけど、算出プロパティ(computed)っていったい何なの?メソッドと何が違うのという疑問を持たれる方もいます。
ここでは、算出プロパティ(computed)の使い方やメリット、メソッド(method)の違いについてまとめています。
computed(算出プロパティ)のメリット
computed(算出プロパティ)には次のようなメリットがあります。
特に最も便利なのは、computedは値の変更があるたびに自動で処理を実行し、その結果を1つだけもつという点です。
このため、computedで定義したプロパティを呼び出すたびに計算処理を行うのではなく、既に計算済みの値を参照します。
一方、メソッドは呼び出される度に処理を実行します。このため、computedの方が処理コストは低くなります。
computedの注意点&data, methods, watchとの違いまとめ
computedはその特性上、dataとmethodsの中間的な存在です。このため記述するときには注意が必要です。
またwatchとはほぼ同じ処理となります。
computedとdataの違い
dataとcomputedで定義したものはそれぞれ {{ (算出)プロパティ名 }}
として呼び出すことができます。ここは非常に似ている点です。
ですが、dataで定義するプロパティの値は、数値、文字列、配列など一意に決まります。
一方、computedで定義もdataと似たようなプロパティを設定しますが、中身は処理になっています。
computedとmethodsの違い
computedとmethodsは処理を行うという点では似ていますが、以下のように大きく異なる特徴を持ちます。
呼び出し方法が違う
computedは基本的に引数をとりません。このため呼び出すときは定義したプロパティ名だけで呼び出します。
一方、methodsで定義したメソッドは呼び出すときにカッコ( )をつけ引数を渡すことができます。
ただし、mehotdsでも、引数を渡さない処理はメソッド名だけで呼び出すこともできます。
実行タイミングが違う(キャッシュを持つかもたないか)
computedは値の変更があるたびに自動で処理を実行します。このため、computedで定義したプロパティを呼び出すたびに計算処理を行うのではなく、既に計算済みの値を参照します。
一方、メソッドは呼び出される度に処理を実行します。このため、computedの方が処理コストは低くなります。
setterを設定できるかが違う
親コンポーネントから渡されたデータを、子コンポーネントでpropsとして受け取ったときに、子のコンポーネントの中で直接そのデータを変更することはできません。
propsのデータの値を変えたい場合はセッター(setter)をセットして、親コンポーネントに対してイベントを送る必要があります。methodsではこのsetterを記述することができませんが、computedではsetterを記述することができます。
computedとwatchの違い
computedの振る舞いは変更があったら検知するという点でwatchとよく似ています。
実際やっている処理も非常に似ています。一番の違いは書き方です。
watchはdataで定義したプロパティを監視対象にするのに対し、computedは監視対象となるプロパティを定義します。
このため、computedの方がwatchよりもより少ないコード量で記述することができます。
computedの使い方
算出プロパティの使い方はmethodsとほぼ同じです。Vueインスタンスの中に、computedオブジェクトを記述して、その中で算出プロパティ名を指定し、処理を記述していきます。
computed:{
算出プロパティ名: function(){
return 処理
}
}
一般的には以下のようにfunctionを省略した記述をしようします。
computed:{
算出プロパティ名(){
return 処理
}
}
HTMLファイル(template内)での呼び出し方
算出プロパティをHTMLファイルや.vueファイルのtemplateタグの中で呼び出す場合は、マスタッシュ展開で算出プロパティ名を記載します。
{{ 算出プロパティ名 }}
呼び出し方は、dataで定義したプロパティと同じです。
算出プロパティの事例
実際に算出プロパティを設定してHTMLファイルで呼び出す例です。
ここでは例として、dataプロパティに設定してあるテキストを反転し、4文字目から10文字目まで表示する処理をcomputedで行います。
.jsの記述
computedを使った記述は次のようになります。
var app = new Vue({
el:"#app",
data:{
greeting:"Hello World!!!"
},
computed:{
revText(){
return this.greeting.split('').reverse().join('').substring(3,10)
}
}
})
算出プロパティ使用・未使用の比較(メリットの実例)
なお、computedを使わない場合と使った場合で、同じ処理を実行するのでもHTMLファイル上での見やすさやコーディング量が大きくことなってきます。
computedを設定した方が、記述が少なく見やすくなります。(computedを使うメリットの一つ)
算出プロパティなしの記述
<body>
<div id="app">
<h2>▼マスタッシュ展開</h2>
<p>
{{ greeting.split('').reverse().join('').substring(3,10)}}
{{ greeting.split('').reverse().join('').substring(3,10)}}
{{ greeting.split('').reverse().join('').substring(3,10)}}
{{ greeting.split('').reverse().join('').substring(3,10)}}
</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="js/main.js"></script>
</body>
算出プロパティで記述
<body>
<div id="app">
<h2>▼computed(算出プロパティ)</h2>
<p>
{{ revText }}
{{ revText }}
{{ revText }}
{{ revText }}
</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="js/main.js"></script>
</body>
算出プロパティを使うことでシンプルに記述することができます。
ブラウザの表示
どちらも同じ処理になっていることがわかります。
算出プロパティとメソッドの違い実例
算出プロパティとメソッドは一見よく似ているように見えますが、違いがあります。
呼び出し方法が違う|computedとmethodsの違い1
呼び出し方
.htmlや.vueファイルのtemplateタグ内で呼び出す場合、メソッドは呼び出し時に「()」が必要なのに対し、算出プロパティは不要です。
<開始タグ>{{ 算出プロパティ名 }}</終了タグ>
<開始タグ>{{ メソッド名() }}</終了タグ>
あくまでメソッドはメソッド、プロパティはプロパティということです。
実例
以下でまったく同じ処理をcomputedとmethodの2つで記述し、それぞれを呼び出します。
メソッド名をrevText2、算出プロパティ名をrevText1とします。
var app = new Vue({
el:"#app",
data:{
greeting:"Hello World!!!"
},
computed:{
revText1: function(){
return this.greeting.split('').reverse().join('').substring(3,10)
}
},
methods:{
revText2: function(){
return this.greeting.split('').reverse().join('').substring(3,10)
}
}
})
HTMLコード
<body>
<div id="app">
<h2>▼computed(算出プロパティ)</h2>
<p>
{{ revText1 }}
</p>
<h2>▼メソッド</h2>
<p>
{{ revText2() }}
</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="js/main.js"></script>
</body>
呼び出しにおいて両者に大きな違いはありません。cmoputedの方がカッコの分だけ2文字少ないです。
ブラウザの表示
どちらも同じ処理が行われていることがわかります。
実行タイミングが違う(キャッシュを持つかもたないか)|computedとmethodsの違い2
computedとmethodsの実行タイミングの違い
computedの算出プロパティも、methodsのメソッドも画面を初回ロードしたときに、それぞれ実行します。
ですが、computedは中の処理で使っているdataプロパティなどの値に変更がなければ、一度実行した処理結果をキャッシュします(保持します)。
すなわち、computedの算出プロパティを呼び出すたびに計算処理を行うのではなく、既に計算済みの値を参照します。
一方、メソッドは呼び出される度に処理を実行します。このため、computedの方が処理コストは低くなります。
実例
computedとmethodによる処理でいかに実行タイミングが異なるかについては、randomメソッドを使うとわかりやすく確認できます。
randomメソッドとはランダムな数値を表示する処理です。Math.random()
として使います。
処理が行われた回数を確認するため、各処理にconsole.logも記述します。
var app = new Vue({
el:"#app",
data:{
basePrice:100
},
computed:{
computedNum: function(){
console.log('computed!')
return Math.random()
}
},
methods:{
methodNum: function(){
console.log('methods!')
return Math.random()
}
}
})
HTML上でそれぞれのメソッドとプロパティを3回ずつ呼び出します。
<body>
<div id="app">
<p>
Computed
</p>
<ol>
<li>{{ computedNum }}</li>
<li>{{ computedNum }}</li>
<li>{{ computedNum }}</li>
</ol>
<p>
Method
</p>
<ol>
<li>{{ methodNum() }}</li>
<li>{{ methodNum() }}</li>
<li>{{ methodNum() }}</li>
</ol>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="js/main.js"></script>
</body>
実行結果
ブラウザの表示
ブラウザの表示を確認すると、computedは全て同じ数値になっています。一方、methodは3回とも別々の数値になっています。
コンソールの表示
console.logを確認すると、computedが実行されたのは1回のみなのに対して、methodは記載した回数だけ実行されていることがわかります。
このため、何度も同じ処理を実行する可能性がある場合は、methodではなく、computedで記載した方が処理回数を減らすことができます。
setterを設定できるかが違う|computedとmethodsの違い3
親コンポーネントから渡されたデータを、子コンポーネントでpropsとして受け取ったときに、子のコンポーネントの中で直接そのデータを変更することはできません。
propsのデータの値を変えたい場合はデータを取得してくるgetter(ゲッター)とデータを変更するsetter(セッター)の2つをあえて記述する必要があります。
setter(セッター)の中で、propsのデータ変更に合わせて親コンポーネントのdataプロパティを書き換えるためのメソッドを発火させます。
methodsではこのsetterを記述することができませんが、computedではsetterを記述することができます。
getterとsetterを使った書き方
getter(ゲッター)とsetter(セッター)を使う時は、computedの算出プロパティの中の処理に、get()というメソッドと、set()というメソッドをそれぞれ設定します。
computed: {
変数名: {
get() {
return this.親から受け取ったデータのプロパティ名
},
set(引数) {
this.$emit('親側で発動させるイベント名', 引数)
}
}
}
get(ゲッター)の詳細
ゲッターは以下のようになっています。親コンポーネントから受け取りpropsで定義したデータをreturnします。
これで、computedで定義した変数に親コンポーネントから受け取ったデータが入ります。
get() {
return this.親から受け取ったデータのプロパティ名;
},
set(セッター)の詳細
セッターは以下のようになっています。
set( 引数 ) {
this.$emit('親側で発動させるイベント名', 引数 );
}
set(引数)
のようにset()の中で定義した引数には、computedで指定した算出プロパティ(変数)の変更後の値が入ります。
引数名に指定はありませんが、第1引数は変更後の新し値ということで「newVal」を使うのが一般的です。
this.$emit('親側で発動させるイベント名', 引数)
は、親コンポーネントのイベントを発動させる処理です。
第1引数で親コンポーネントで発動させるイベント名を指定します。第2引数がイベントに渡すデータです。
親コンポーネント側の追加処理
this.$emit('親側で発動させるイベント名', 引数)
で、親コンポーネントのイベントを発動させる処理を追加しています。
このため、親コンポーネントに「イベント」と「イベントで発火するメソッド」を作成する必要があります。
イベントの設置
子コンポーネントを呼び出しているタグの中で、v-onディレクティブ(または@)を使ってイベントを設定します。
このときイベント名は子コンポーネントの$emitで設定した内容と合わせます。
<子コンポーネントタグ
@イベント名="メソッド名"
>
メソッドの設置
続いて、イベントが発火したときに実行するメソッドを定義します。
methods:{
メソッド名(引数){
this.プロパティ名 = 引数
}
}
引数の部分には、$emitイベントの第2引数で渡したデータが入ります。
以上で設定は完了です。
実例
親コンポーネントのデータ「xxx」を子コンポーネントの中でpropsとして受け取ります。
子コンポーネントの中にcomputedを使って算出プロパティ「yyy」を定義し、ゲッターで「xxx」を設定します。(「yyy」=「xxx」とする)
「yyy」をv-modelで画面上で編集できるようにして、「yyy」に変更があった場合はcomutedのセッターで親コンポーネントの「xxx」自体を書き換えるという処理は以下のようになります。
親コンポーネントの中身
<template>
<div class="hello">
<UserDetail
:xxx="xxx"
@changeXxx="changeXxx"
/>
<div>xxxの値:{{xxx}}</div>
</div>
</template>
<script>
import UserDetail from './UserDetail'
export default {
name: 'HelloWorld',
components: {
UserDetail
},
data(){
return{
xxx: "secret"
}
},
methods:{
changeXxx(newVal){
this.xxx = newVal
}
}
}
</script>
<style scoped>
省略
</style>
子コンポーネントを読み込んでいる場所に@changeXxx="changeXxx"
というイベントとメソッドを定義しています。
子コンポーネントの$emitでchangeXxxイベントが発火すると指定したメソッドが実行されます。(※ここではイベント名とメソッド名を一致させていますが、異なってても問題ありません)
メソッドは次のようになってます。
methods:{
changeXxx(newVal){
this.xxx = newVal
}
}
$eventで送られてきた第2引数のデータを引数として受け取り、プロパティに代入します。
子コンポーネントの中身
<template>
<div id="user-detail">
<h1>ユーザーの詳細ページです</h1>
<ul class="profile-list">
<li>xxx: <input type="text" v-model="yyy"></li>
</ul>
<p></p>
</div>
</template>
<script>
export default {
props:{
xxx: {type: String}
},
computed:{
yyy:{
get(){
return this.xxx
},
set(newVal){
this.$emit("changeXxx", newVal)
}
}
}
}
</script>
<style scoped>
省略
</style>
computedでyyyという新しいプロパティを作成しています。
computed:{
yyy:{
get(){
return this.xxx
},
set(newVal){
this.$emit("changeXxx", newVal)
}
}
}
inputタグのv-modelで双方向バインディングするプロパティもこのcomputedで定義したyyyに変更しています。
<input type="text" v-model="yyy">
ブラウザの表示
デフォルトでは以下のように表示されます。
子コンポーネントの中で表示している「secret」の文字を、「changed」に変えると、親コンポーネントの要素として下部に表示している「xxxの値」の部分も変更され、正しく動作します。
computedとwatchの違い
computedの振る舞いは変更があったら検知するという点でwatchとよく似ています。実際やっている処理も非常に似ています。
一番の違いは書き方です。
watchはdataで定義したプロパティを監視対象にするのに対し、computedは監視対象となるプロパティを定義します。
このため、computedの方がwatchよりもより少ないコード量で記述することができます。
例えば、画面上に入力された「名前」と「苗字」から「フルネーム」を作成する処理を作成する場合は以下のようになります。
watchを使った記述
watchを使う場合は、dataで「名前(firstName)」「苗字(lastName)」「フルネーム(fullName)」の3つのプロパティを設定する必要があります。
watchの処理自体も、「名前(firstName)」「苗字(lastName)」のそれぞれの変更を検知する処理を記述する必要があります。
var app = new Vue({
el:"#app",
data:{
firstName:'',
lastName:'',
fullName:''
},
watch:{
firstName(value){
this.fullName = value + " " + this.lastName
},
lastName(value){
this.fullName = this.lastName + " " + this.firstName
}
}
})
computedを使った記述
computedを使う場合は、dataで定義するのは「名前(firstName)」「苗字(lastName)」の2つのみです。
算出プロパティとして「フルネーム(fullName)」を設定します。
また、算出プロパティ「fullName」の中では「名前(firstName)」と「苗字(lastName)」それぞれの変更を検知するので、処理を1行で書くことができます。
var app = new Vue({
el:"#app",
data:{
firstName:'',
lastName:'',
},
computed:{
fullName(){
return this.firstName + " " + this.lastName
}
}
})
上記のようにかなりシンプルに記述することができます。
HTMLのコード
html部分のコードはどちらの場合も同じです。
<body>
<div id="app">
<p>
firstName: <input type="text" v-model:value="firstName">
</p>
<p>
lastName: <input type="text" v-model:value="lastName">
</p>
<p>
fullName: {{ fullName }}
</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</body>
ブラウザの表示
なおブラウザの表示はどちらも以下のようになります。
デフォルトでは「firstName」と「lastName」に何も表示されていません。
↓ 「firstName」に「Steve」と入力
「fullName」の横に入力した内容が表示されます。
↓ 「lastName」に「Jobs」と入力
「fullName」の横に入力した内容が表示されます。