sitateru tech blog: リアクティブ

sitateru tech blog

シタテルの技術やエンジニアの取り組みを紹介するテックブログです。

ラベル リアクティブ の投稿を表示しています。 すべての投稿を表示
ラベル リアクティブ の投稿を表示しています。 すべての投稿を表示

2018年11月29日木曜日

Vueでwatchが動いてくれない

11月 29, 2018

こんにちは、シタテルの藤本です。
主にSCS(Sitateru-Control-System)という生産管理システムのバックエンド(Rails)を担当しています。

SCSではRailsでのViewは基本hamlで書かれていますが現在Vueへと移行しようとしています。
最近私もVueを触る機会があり、watchを使用していてハマってしまったところを2点ほど書いておきます。

  1. Objectをwatchするときはdeepをつけよう
  2. = で追加してはリアクティブになりませんよ

1. Objectをwatchするときはdeepをつけよう

以下のようにdata(user_nameusers)がいて
その値が変更されたことを監視するようにしたい場合にwatchを以下のように書きます

  data () {
    return {
      user_name: '太郎',
      users: {}
    }
  }

  watch: {
    user_name: function (new_value, old_value) {
      alert(`user_name watch!!${this.user_name}`)
    },
    users: function (new_value, old_value) {
      alert(`users watch!!${this.users['name']}`)
    }
  }

  methods: {
    changeJiro () {
      this.user_name = '二郎'    
      this.$set(this.users, 'name', '二郎')    
    },
    changeSaburo () {
      this.user_name = '三郎'    
      this.$set(this.users, 'name', '三郎')
    }
  }

上記の状態でchangeJiro or changeSaburoを呼び出すと
user_nameしか監視されません。

Objectを監視したい場合には以下のようにwatchに対してdeepオプションを付与する必要があります。

    users: {
      handler: function (new_value, old_value) {
        alert(`users watch!!${this.users['name']}`)
      },
      deep: true
    }

公式の以下に記載があります。
watchについて

2. = で追加してはリアクティブになりませんよ

もう1点ハマったところでObjectの値をセットする際の処理の仕方です。

  1. とほとんど同じ内容ですがmethodsでのusersへのセットをuser_nameと同様に=に変更しています。

    data () {
     return {
       user_name: '太郎',
       users: {}
     }
    }
    
    watch: {
     user_name: function (new_value, old_value) {
       alert(`user_name watch!!${this.user_name}`)
     },
     users: {
       handler: function (new_value, old_value) {
         alert(`users watch!!${this.users['name']}`)
       },
       deep: true
     }
    }
    
    methods: {
     changeJiro () {
       alert(`check!! ${this.users['name']}`)
       this.user_name = '二郎'
       this.users['name'] = '二郎' //←ここが=でのセットに変わっている
     },
     changeSaburo () {
       alert(`check!! ${this.users['name']}`)
       this.user_name = '三郎'
       this.users['name'] = '三郎' //←ここが=でのセットに変わっている
     }
    }

こちらも同様にchangeJiro or changeSaburoを呼び出すと
user_nameしか監視されません。
ただしusers['name']を確認してみると値はセットされています。

原因としてはVue.jsはプロパティの追加または削除を検出できず、リアクティブになっていないため監視できないとのことです。
対応方法としてはわかっているもので2つあります。

  1. 最初から定義しておく
  2. setにて追加を行う

1. 最初から定義しておく

Objectのプロパティを最初から定義しておくことで監視することが出来るようになります。
dataに定義しているusersを以下のように修正します。

users: { name: '太郎' }

2. setにて追加を行う

こちらは1. に書いてある通りで、methodを以下のように修正します。

changeJiro () {
  this.user_name = '二郎'    
  this.$set(this.users, 'name', '二郎')    
},
changeSaburo () {
  this.user_name = '三郎'    
  this.$set(this.users, 'name', '三郎')
}

もっと詳細については以下をご参照ください。
リアクティブの探求

また配列に関しても同様の注意が必要で以下に詳しく書かれております。
配列の変化を検出

終わりに

1人でわりといい時間考え込んでましたが周りに聞いたら即解決しました。
悩みすぎるのはよくないと反省すると共に心強い味方がいることに大変感謝した次第です。
またVueの公式は大変日本語でも充実しているのでしっかり読もうと思いました。

以上です、これからVueをご利用される方の助けになれば幸いです。