算出プロパティの基本
- 算出プロパティ(computed)
- 関数によって算出したデータを返すことができるプロパティ
通常の記述(文字の反転)
<div id="app">
<p>{{ message.split('').reverse().join('') }}</p>
</div>
const app = Vue.createApp({
data: () => ({
message: 'Hello Vue.js!'
})
app.mount('#app')
- このように書けるが、テンプレートの中にたくさんのロジックを詰め込むとコードが肥大化して見づらくなりメンテナンス性が悪くなる
- メッセージの反転を別の場所でも行うとすると再利用ができない
- 複雑なロジックを実行する時や、ロジックの再利用性を高めたい時には算出プロパティを利用することが推奨される
算出プロパティを利用
<div id="app">
<p>{{ message.split('').reverse().join('') }}</p>
<p>{{ reversedMessage }}</p>
</div>
const app = Vue.createApp({
data: () => ({
message: 'Hello Vue.js!'
}),
computed: {
reversedMessage: function(){
return this.message.split('').reverse().join('')
}
}
})
app.mount('#app')
js側
- computed`オプションを追加
- 算出プロパティ名
reversedMessage
- functionの書き方は固定
-
return.this
でhtml側に記述していた処理を持ってくる
→return this.message.split('').reverse().join('')
html
- マスタッシュ構文で算出プロパティ名
reversedMessage
を指定
算出プロパティとメソッドの比較
- 算出プロパティを使わなくてもメソッドを使えばよいのでは?
最初の状態(算出プロパティでテキスト反転)
<div id="app">
<p>{{ reversedMessage }}</p>
</div>
const app = Vue.createApp({
data: () => ({
message: 'Hello Vue.js!'
}),
computed: {
reversedMessage: function(){
return this.message.split('').reverse().join('')
}
}
})
app.mount('#app')
メソッドを追記した形
<div id="app">
<p>{{ reversedMessage }}</p>
<p>{{ reversedMessageMethod() }}</p>
</div>
const app = Vue.createApp({
data: () => ({
message: 'Hello Vue.js!'
}),
computed: {
reversedMessage: function(){
return this.message.split('').reverse().join('')
}
},
methods:{
reversedMessageMethod: function(){
return this.message.split('').reverse().join('')
}
}
})
app.mount('#app')
- どっちでも表示できる
算出プロパティとメソッドの違い①
-
computed(プロパティ)
→プロパティなので()不要 -
methods(メソッド)
→メソッドなので()必要
算出プロパティとメソッドの違い②
-
computed
→getter、setterどちらも定義できる -
methods
→getterのみ定義できる
算出プロパティとメソッドの違い③
キャッシュ
-
computed
→キャッシュ有り -
methods
→キャッシュ無し -
computedの場合はmessageプロパティが変わらない限りはreversedMessageに何度アクセスしても関数を再び実行することなく以前計算された結果を瞬時に返す
-
methodsの場合は呼び出されるたびに関数の処理を行って処理を返す
-
この違いは巨大な配列を扱ったりするときにパフォーマンスに影響することがある
算出プロパティのgetterとsetterを使う
- 税抜き価格、税込み価格の計算を例に行う
<div id="app">
<p>bass price: <input type="text" v-model="basePrice"></p>
<p>tax included price: <input type="text" v-model="taxIncludedPrice"></p>
</div>
const app = Vue.createApp({
data: () => ({
basePrice: 100
}),
computed:{
taxIncludedPrice:{
get: function(){
return this.basePrice * 1.1
},
set: function(value){
this.basePrice = value / 1.1
}
}
}
})
app.mount('#app')
- 300の1.1倍が記入される
- getterとsetterがどちらも使える
- getter、setterの説明になっていないので確認必要
算出プロパティのキャッシュ動作を確認する
- computed→キャッシュ有り
- methods→キャッシュ無し
ランダムの数字を返す処理をそれぞれ記述
- 算出プロパティ(computed)
- メソッド(methods)
<div id="app">
<h2>computed:</h2>
<ol>
<li>{{ computedNumber }}</li>
<li>{{ computedNumber }}</li>
<li>{{ computedNumber }}</li>
</ol>
<h2>methods:</h2>
<ol>
<li>{{ methodsNumber() }}</li>
<li>{{ methodsNumber() }}</li>
<li>{{ methodsNumber() }}</li>
</ol>
</div>
const app = Vue.createApp({
data: () => ({
}),
computed:{
computedNumber: function(){
return Math.random()
}
},
methods: {
methodsNumber: function(){
return Math.random()
}
}
})
app.mount('#app')
- computedはキャッシュがあるので同じ値が出力される
- methodsは毎回出力される
- console.logなどの同様にキャッシュされる
- キャッシュしたい、したくないで処理を分けることができる
監視プロパティ
- 監視プロパティはウォッチャとも呼ばれる
- 特定のデータまたは、算出プロパティの状態を監視して、変化があったときに登録した処理を自動的に実行できるもの
- 【例】検索フォームの値が変わったタイミングで自動的にAjaxを行って結果を一覧表示する
監視プロパティの基本
- messageプロパティのデータが変更されたら、コンソールに、旧値と、新値を表示する。
<div id="app">
<p>{{ message }}</p>
<p>
<input type="text" v-model="message">
</p>
</div>
const app = Vue.createApp({
data: () => ({
message: 'Hello Vue.js!'
}),
watch:{
message: function(newValue, oldValue){
console.log('new: %s, old: %s', newValue, oldValue)
}
}
})
app.mount('#app')
- このように通常通り入力欄と同期してテキストが表示される
- ↑↑↑入力ごとに、入力変化後の値、入力変化前の値のどちらも出力する状態になっている
- messageプロパティの値に変化があったらコンソールにログを出力、という処理
- ※aと入力すると
new: Hello Vue.js!a
←変化後、old: Hello Vue.js!
←変化前
【例題】単位変換アプリを作る
- 長さkm、mを相互変換するアプリ
動きのチェック
<div id="app">
<p><input type="text" v-model="km">km</p>
<p><input type="text" v-model="m">m</p>
</div>
const app = Vue.createApp({
data: () => ({
km: 0,
m: 0
}),
watch:{
km: function(value){
console.log(value)
}
}
})
app.mount('#app')
- ↑↑↑入力された値が、データが変更されるたびに呼ばれている
-
function(value)
のvalue
はページ内のkmの入力欄の値 - watchは指定したプロパティの変化ごとに処理は発火させてくれる(のだと思う)
km、mを用いた実装
<div id="app">
<p><input type="text" v-model="km">km</p>
<p><input type="text" v-model="m">m</p>
</div>
const app = Vue.createApp({
data: () => ({
km: 0,
m: 0
}),
watch:{
km: function(value){
console.log(value)
this.km = value
this.m = value * 1000
},
m: function(value){
this.km = value / 1000
this.m = value
}
}
})
app.mount('#app')
- ↑↑↑kmに1を入れるとmに1000が入る
- ↑↑↑2を入れるとmに2000が入るような実装
算出プロパティと監視プロパティの比較
- どちらでも実装できる場合、基本的には算出プロパティの利用を推奨
- シンプルに記述できるため
【例題】
- ユーザーに名字lastNameと名前firstNameを別々のテキスト入力欄で入力させる
- フルネームをfirstName+スペース+lastNameの形式で表示する
- 入力値に変化があるたびに、フルネーム表示を更新する
- 算出プロパティと監視プロパティでそれぞれ実装
監視プロパティを利用した記述
<div id="app">
<p><input type="text" v-model="firstName"></p>
<p><input type="text" v-model="lastName"></p>
<p>fullName; {{ fullName }}</p>
</div>
const app = Vue.createApp({
data: () => ({
firstName: '',
lastName: '',
fullName: ''
}),
watch:{
firstName: function(value){
this.fullName = value + ' ' + this.lastName
},
lastName: function(value){
this.fullName = this.firstName + ' ' + value
}
}
})
app.mount('#app')
- firstNamae、lastNameそれぞれ出力される
算出プロパティを利用した記述
<div id="app">
<p><input type="text" v-model="firstName"></p>
<p><input type="text" v-model="lastName"></p>
<p>fullName; {{ fullName }}</p>
</div>
const app = Vue.createApp({
data: () => ({
firstName: '',
lastName: ''
}),
computed:{
fullName: function(){
return this.firstName + ' ' + this.lastName
}
}
})
app.mount('#app')
- 監視プロパティと同じ実装になっているが記述は算出プロパティの方が簡潔に書かれている
オプション【deep】
- 監視プロパティにはオプションが設定できる
- deepオプションは複数あるうちのひとつ
- deepオプションはネストされたオブジェクトも監視する場合にtrueを設定する
通常
<div id="app">
<ul>
<li v-for="color in colors">
{{ color.name }}
</li>
</ul>
<button v-on:click="onClick">Click!</button>
</div>
const app = Vue.createApp({
data: () => ({
colors:[
{ name: 'Red'},
{ name: 'Green'},
{ name: 'Blue'}
]
}),
watch:{
colors:{
handler: function(newValue, oldValue){
console.log('Update!')
}
}
},
methods:{
onClick: function(event){
this.colors[1].name = 'White'
}
}
})
app.mount('#app')
- Click!を押すと「Green」が「White」に変わる
- 通常の処理はできている
- しかしコンソールに「Update!」と表示されていない状態になっている
- colorsの内容が変わっているのにcolorプロパティの変更がフックできていない
- 監視プロパティがデフォルトの設定ではプロパティの値がネストしている場合には深い部分の値の変更は監視されない
- colorsという配列の中にあるオブジェクトのkeyがnameのvalueが変更されても検知されない
- 監視したいプロパティのネストの深さに関係なく変更を検知するためにはdeepオプションをtrueに設定する
const app = Vue.createApp({
data: () => ({
colors:[
{ name: 'Red'},
{ name: 'Green'},
{ name: 'Blue'}
]
}),
watch:{
colors:{
handler: function(newValue, oldValue){
console.log('Update!')
},
deep: true
}
},
methods:{
onClick: function(event){
this.colors[1].name = 'White'
}
}
})
app.mount('#app')
- ボタンを押すと「Update!」が表示されるようになった