ガイド
基本的な使い方
- インストール
- はじめに
- Vue インスタンス
- テンプレート構文
- 算出プロパティとウォッチャ
- クラスとスタイルのバインディング
- 条件付きレンダリング
- リストレンダリング
- イベントハンドリング
- フォーム入力バインディング
- コンポーネントの基本
コンポーネントの詳細
- コンポーネントの登録
- プロパティ
- カスタムイベント
- スロット
- 動的 & 非同期コンポーネント
- 特別な問題に対処する
トランジションとアニメーション
- Enter/Leave とトランジション一覧
- 状態のトランジション
再利用と構成
- ミックスイン
- カスタムディレクティブ
- 描画関数とJSX
- プラグイン
- フィルター
ツール
- 単一ファイルコンポーネント
- テスト
- TypeScript のサポート
- プロダクション環境への配信
スケールアップ
- ルーティング
- 状態管理
- サーバサイドレンダリング
- セキュリティ
内部
- リアクティブの探求
移行
- Vue 1.x からの移行
- Vue Router 0.7.x からの移行
- Vuex 0.6.x から 1.0 への移行
その他
- 他のフレームワークとの比較
- Vue.js コミュニティへ参加しましょう!
- チームに会おう
v2.x 以前のドキュメントです。 v3.x のドキュメントを見たい場合はこちら
算出プロパティとウォッチャ
最終更新日: 2019年4月8日
算出プロパティ
テンプレート内に式を書けるのはとても便利ですが、非常に簡単な操作しかできません。テンプレート内に多くのロジックを詰め込むと、コードが肥大化し、メンテナンスが難しくなります。例えば:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
こうなってくると、テンプレートはシンプルでも宣言的でもなくなってしまっています。しばらく眺めて、やっとこれが message
を逆にして表示していることに気付くでしょう。逆にしたメッセージをテンプレートの中で 2 回以上使おうとすると、問題はより深刻になります。
上記の理由から、複雑なロジックには算出プロパティを利用すべきです。
基本的な例
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 算出 getter 関数
reversedMessage: function () {
// `this` は vm インスタンスを指します
return this.message.split('').reverse().join('')
}
}
})
結果:
Original message: "{{ message }}"
Computed reversed message: "{{ reversedMessage }}"
ここでは、算出プロパティ reversedMessage
を宣言しました。私たちが提供した機能は、プロパティ vm.reversedMessage
に対する getter 関数として利用されます:
console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'
コンソールを開いて、vm で遊んでみてください。vm.reversedMessage
の値は、常に vm.message
の値に依存しています。
通常のプロパティと同じように、テンプレート内の算出プロパティにデータバインドすることもできます。Vue は vm.reversedMessage
が vm.message
に依存していることを知っているので、vm.message
が変わると vm.reversedMessage
に依存する全てのバインディングを更新します。さらに、最も良いところは、この依存関係が宣言的に作成されていることです。算出 getter 関数は副作用がないので、テストや値の推論が容易になります。
算出プロパティ vs メソッド
こういった式を持つメソッドを呼び出すことで、同じ結果が実現できることに気付いたかもしれません:
<p>Reversed message: "{{ reverseMessage() }}"</p>
// コンポーネント内
methods: {
reverseMessage: function () {
return this.message.split('').reverse().join('')
}
}
算出プロパティの代わりに、同じような関数をメソッドとして定義することも可能です。最終的には、2つのアプローチは完全に同じ結果になります。しかしながら、算出プロパティはリアクティブな依存関係にもとづきキャッシュされるという違いがあります。算出プロパティは、リアクティブな依存関係が更新されたときにだけ再評価されます。これはつまり、message
が変わらない限りは、reversedMessage
に何度アクセスしても、関数を再び実行することなく以前計算された結果を即時に返すということです。
Date.now()
はリアクティブな依存ではないため、次の算出プロパティは二度と更新されないことを意味します:
computed: {
now: function () {
return Date.now()
}
}
対称的に、メソッド呼び出しは、再描画が起きると常に関数を実行します。
なぜキャッシングが必要なのでしょうか?巨大な配列をループしたり多くの計算を必要とする、コストの高い A という算出プロパティがあることを想像してみてください。A に依存する他の算出プロパティもあるかもしれません。その場合、キャッシングがなければ必要以上に A の getter を実行することになってしまいます。キャッシングしたくない場合は、代わりにメソッドを使いましょう。
算出プロパティ vs 監視プロパティ
Vue は Vue インスタンス上のデータの変更を監視し反応させることができる、より汎用的な 監視プロパティ(watched property) を提供しています。他のデータに基づいて変更する必要があるデータがある場合、特に AngularJS に慣れていたら、watch
を多く利用したいと思うかもしれません。しかし、命令的な watch
コールバックよりも、多くの場合では算出プロパティを利用するほうが良いでしょう。次の例で考えてみましょう:
<div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
上記のコードは命令的で冗長です。算出プロパティを利用したバージョンと比較してみましょう:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
こちらの方が、はるかに良くありませんか?
算出 Setter 関数
算出プロパティはデフォルトでは getter 関数のみですが、必要があれば setter 関数も使えます:
// ...
computed: {
fullName: {
// getter 関数
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter 関数
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
vm.fullName = 'John Doe'
を呼ぶと、setter 関数が呼び出され、vm.firstName
と vm.lastName
が適切に更新されます。
ウォッチャ
多くの場合では算出プロパティの方が適切ではありますが、カスタムウォッチャが必要な時もあるでしょう。データの変更に対して反応する、より汎用的な watch
オプションを Vue が提供しているのはそのためです。データが変わるのに応じて非同期やコストの高い処理を実行したいときに最も便利です。
例:
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- ajax ライブラリの豊富なエコシステムや、汎用的なユーティリティ -->
<!-- メソッドがたくさんあるので、Vue のコアはそれらを再発明せずに -->
<!-- 小さく保たれています。この結果として、慣れ親しんでいるものだけを -->
<!-- 使えるような自由さを Vue は持ち合わせています。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
el: '#watch-example',
data: {
question: '',
answer: 'I cannot give you an answer until you ask a question!'
},
watch: {
// この関数は question が変わるごとに実行されます。
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// _.debounce は特にコストの高い処理の実行を制御するための
// lodash の関数です。この場合は、どのくらい頻繁に yesno.wtf/api
// へのアクセスすべきかを制限するために、ユーザーの入力が完全に
// 終わるのを待ってから ajax リクエストを実行しています。
// _.debounce (とその親戚である _.throttle ) についての詳細は
// https://lodash.com/docs#debounce を見てください。
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
},
methods: {
getAnswer: function () {
if (this.question.indexOf('?') === -1) {
this.answer = 'Questions usually contain a question mark. ;-)'
return
}
this.answer = 'Thinking...'
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = 'Error! Could not reach the API. ' + error
})
}
}
})
</script>
結果:
Ask a yes/no question:
{{ answer }}
この場合では、watch
オプションを利用することで、非同期処理( API のアクセス)の実行や、処理をどのくらいの頻度で実行するかを制御したり、最終的な answer が取得できるまでは中間の状態にしておく、といったことが可能になっています。これらはいずれも算出プロパティでは実現できません。
watch
オプションに加えて、命令的な vm.$watch API を利用することもできます。