Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

vue入門04(算出プロパティ・監視プロパティ)

Last updated at Posted at 2024-05-30

算出プロパティの基本

  • 算出プロパティ(computed)
  • 関数によって算出したデータを返すことができるプロパティ

通常の記述(文字の反転)

<div id="app">
  <p>{{ message.split('').reverse().join('') }}</p>
</div>
const app = Vue.createApp({
  data: () => ({
    message: 'Hello Vue.js!'
})
app.mount('#app')

スクリーンショット 2024-05-30 215006.png

  • このように書けるが、テンプレートの中にたくさんのロジックを詰め込むとコードが肥大化して見づらくなりメンテナンス性が悪くなる
  • メッセージの反転を別の場所でも行うとすると再利用ができない
  • 複雑なロジックを実行する時や、ロジックの再利用性を高めたい時には算出プロパティを利用することが推奨される

算出プロパティを利用

<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')

スクリーンショット 2024-05-30 215945.png

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')

スクリーンショット 2024-05-30 222316.png

メソッドを追記した形

<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')

スクリーンショット 2024-05-30 222654.png

  • どっちでも表示できる

算出プロパティとメソッドの違い①

  • 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')

スクリーンショット 2024-05-30 225403.png

  • 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')

スクリーンショット 2024-05-30 230454.png

  • 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')

スクリーンショット 2024-06-02 124741.png

  • このように通常通り入力欄と同期してテキストが表示される

スクリーンショット 2024-06-02 124902.png
スクリーンショット 2024-06-02 124910.png
スクリーンショット 2024-06-02 124916.png

  • ↑↑↑入力ごとに、入力変化後の値、入力変化前の値のどちらも出力する状態になっている
  • 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')

スクリーンショット 2024-06-02 130513.png

  • ↑↑↑入力された値が、データが変更されるたびに呼ばれている
  • 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')

スクリーンショット 2024-06-02 130055.png
スクリーンショット 2024-06-02 130104.png

  • ↑↑↑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')

スクリーンショット 2024-06-02 132540.png

  • 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')

スクリーンショット 2024-06-02 133005.png

  • 監視プロパティと同じ実装になっているが記述は算出プロパティの方が簡潔に書かれている

オプション【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')

スクリーンショット 2024-06-02 134223.png
スクリーンショット 2024-06-02 134227.png

  • Click!を押すと「Green」が「White」に変わる
  • 通常の処理はできている

スクリーンショット 2024-06-02 134340.png

  • しかしコンソールに「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')

スクリーンショット 2024-06-02 134815.png
スクリーンショット 2024-06-02 134820.png

  • ボタンを押すと「Update!」が表示されるようになった
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?