第3回 – Vue.jsのライフサイクル等の簡易逆引きリファレンス

2019.04.22

最近お腹周りのお肉が気になりまして、2,3年前までは摘むことができなかったお腹が摘めるように…。
一念発起し、筋トレを始めたむつたくです。
娘も手伝ってくれて、負荷を(主に乗ってきます…)掛けてくれます。良い意味で筋肉が悲鳴をあげてます。

 

さて、今回ですが、前回のブログでAPI呼び出しとかを〜とお伝えしてましたが、それよりも大事なライフサイクルやcomputed、methodsとかのことを説明した方がいいなと思いまして、急遽変更致します。
ここを理解しないと、なんでこうなる???というのが多発すると思います。

 

それともう一つ!
chromeを使用している方はVue.js devtoolsを追加しておくとリアクティブの状態とか可視化できますので、少し幸せになれます。

 

 

項目

  • Vue.jsのライフサイクル

– Vueインスタンス生成
– DOMへのマウント
– データ変更/画面の更新
– Vueインスタンス破棄
– エラー捕捉

  • createdとmountedのユースケース
  • 算出プロパティと監視プロパティ(computed/watch)

– 算出プロパティ(computed)
– 監視プロパティ(watch)

 

  • メソッド(methods)

 

今回は、上記4つのことについて触れていきますね。

 

Vue.jsのライフサイクル

ライフサイクルというのは、インスタンスが生成(初期化)されてから破棄されるまでのことになります。
Vue.jsでは以下のサイクルになってます。

Vue.jsのライフサイクルは上の画像でもわかるように4段階と記載はされていませんが、error出力に関する1つに分かれてます。

 

(1) Vueインスタンスの生成(初期化)

発生フック:  beforeCreate   created 

1段階目です。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
  {省略}
</template>
<script>
export default{
  beforeCreate() {
    // リアクティブデータ作成前に行いたい処理
  },
  created() {
    // リアクティブデータ作成後に行いたい処理
  }
}
<script>

まず、Vueインスタンスが作成されます。具体的には   new Vue()  された時です。その際に発生フックの2つが呼び出されます。インスタンスが作成された段階なので、この時点では、まだコンポーネント(DOM)は反映されてません。
リアクティブデータが初期化される前にbeforeCreateが呼び出され、リアクティブデータが初期化された後にcreatedが呼ばれます。

 

(2) DOMへのマウント

発生フック: beforeMount   mounted 

2段階目です。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
  {省略}
</template>
<script>
export default{
  beforeMount() {
    // DOMにマウントされる前に行いたい処理
  },
  mounted() {
    // DOMにマウントされた後に行いたい処理
  }
}
<script>

生成されたVueインスタンスは次にDOMにマウントされます。
これは、コンポーネントがHTML要素の一員として画面に描画されます。
マウント前にbeforeMountが呼び出され、マウント後にmountedが呼び出されます。したがって、DOMにアクセス可能なのは、mounted以降になります。

 

(3) データ変更/画面の更新

発生フック: beforeUpdate   updated 

3段階目です。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
  {省略}
</template>
<script>
export default{
  beforeUpdate() {
    // DOMが更新される前に行いたい処理
  },
  updated() {
    // DOMを更新した後に行いたい処理
  }
}
<script>

Vueインスタンスdataが変更された場合や、propsで渡されているデータが親で変更された場合など、それらの値の変更のタイミングでDOMを自動的に更新し再描画します。
DOMが更新(画面が再描画)される前にbeforeUpdateが呼び出され、DOMが更新された後にupdatedが呼び出されます。
ちなみにView全体が描画されるまで待機するnextTickと言うのもあります。

1
2
3
4
5
6
7
// データを変更する
vm.msg = 'Hello'
// DOM は未更新
Vue.nextTick(function () {
  // DOM 更新済
  // 対象DOMに属性を加える etc...
})

 

(4) Vueインスタンス破棄

発生フック: beforeDestroy   destroyed 

4段階目です。

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
  {省略}
</template>
<script>
export default{
  beforeDestroy() {
    // Vueインスタンスを破棄する前に行いたい処理
  },
  destroyed() {
    // Vueインスタンスを破棄した後に行いたい処理
  }
}
<script>

v-ifv-forrouter.pushなどの機能によりインスタンス表示されなくなる(破棄される)タイミングで上記2つがフックされます。
Vueインスタンスが破棄される前にbeforeDestroyが、Vueインスタンスが破棄された後にDestroyedが呼び出されます。

 

(5) エラー捕捉

発生フック: errorCaptured 

+1つの情報です。

任意の子孫コンポーネントからエラーをキャッチした時に呼び出されます。
このフックは

  • エラー内容
  • エラーをトリガするVueインスタンス
  • エラーをキャッチした捕捉場所

これら3つの引数を受け取ります。

 

親コンポーネント

1
2
3
4
5
6
errorCaptured (err, vm, info) {
  // エラー画面やダイアログを表示するなど
  // err : エラー内容
  // vm  : エラーをトリガするVueインスタンス
  // info: エラーをキャッチした捕捉場所
}

 

 

created と mounted のユースケース

Vue.jsの開発において、おそらく最も使用されているのが createdmouted ではないかと思います。
違いは前節でも記載しましたが、DOMにアクセスできるかできないか。

 

createdのユースケース

Ajax通信を行い、データを初期化する

DBと繋がるSPAを作成する際には、頻出なパターンだと思います。
mountedで行っても、同様の結果が返ってきますが、なぜmoutedではなく
ここでAjax通信を行うかと言いますと、独断ですか、createdがフックされるタイミングが僅かであれ先ですよね?
Ajax通信は非同期が基本だと思いますので、少しでも早く通信して反映までの処理時間を減らした方がいいと思います。

DOMアクセスする必要が無いものに関しては、ここで行ってしまい、DOMアクセスする必要があるものはmoutedで行うようにすれば良いと思います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
  {省略}
</template>
<script>
@import axios from 'axios'
export default{
  data() {
    return {
      items[]
    }
  },
  created() {
    axios.get('APIのURL')
     .then(res => {
       this.items = res.data
     })
     .catch(err => {
       // エラー出力処理
     })
  }
}
</script>

 

moutedのユースケース

プラグインの初期化を行う

便利なJavaScriptライブラリがたくさんありますが、基本的にターゲットとなるDOMが描画されてないといけません。createdではまだ描画されてませんよね?アクセスできないので、mountedに初期化処理を書きます。

また、Ajax通信をここで行ってもいいのですが、DOMにアクセスする必要があるものに対してだけに限定した方が良いです。描画されてないからエラーに・・・なんてことも有り得ます。

 

 

算出プロパティと監視プロパティ(computed/watch)

(1) 算出プロパティ(computed)

算出プロパティとは、プロパティなのに関数も使えるという不思議な機能です。
関数の演算結果をプロパティの値として用いるという特徴を持ちます。
また、値をキャッシュしますので、値に変更が無いとキャッシュされたものがそのまま使用されるので、描画の処理時間が短縮できます。
ただし、引数を使用しますと、依存関係がなくなってしまうので、毎回再描画するハメになります。

 

算出プロパティのメリットは、加工したデータを渡す事(getter)任意の加工をしてデータを保存する事(setter)、
そしてtemplate内の可読性が高まることです。

 

(2) 監視プロパティ

データや算出プロパティの状態を監視して、変化があったとき登録した処理を自動実行します。
つまり、データの変化をトリガにしたフック
です。

データ変化であれば、算出プロパティのsetter/getterで十分かと思いますが、非同期やコストの高い(重い)処理を実行したいときに最も便利だと思います。

 

 

メソッド(methods)

振る舞いは基本的に算出プロパティと同様です。
違いは、算出プロパティはデータ変更がトリガなのに対し、methodsは、DOMイベントを呼び出す際に
 v-on  ディレクティブを使用します。また、値をキャッシュしないので、同じ結果になることになっても、毎回処理が実行されることになります。
また、v-onは省略できます。

1
2
3
<!-- どちらも同じ -->
<button type="button" name="btn" v-on:click="buttonclick()"></button>
<button type="button" name="btn" @click="buttonclick()"></button>

 

算出プロパティ(computed)とメソッド(methods)の違いは以下の感じでしょう。

 

テンプレート

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
  <div>
    <div>
      <input type="text" v-model="text"/>
    </div>
    <div>
      computed: {{reversedMessageComputed}}
    </div>
    <div>
      methods: {{reversedMessageMethods()}}
    </div>
    <div>
    <button @click="incrementCount()">count: {{count}}</button>
    </div>
  </div>
</template>

 

スクリプト

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
export default {
  data () {
    return {
      text: 'input text',
      count: 0,
    }
  },
  computed: {
    reversedMessageComputed () {
      console.log('computed:' + this.text)
      return this.text.split('').reverse().join('')
    }
  },
  methods: {
    reversedMessageMethods () {
      console.log('methods:' + this.text)
      return this.text.split('').reverse().join('')
    },
    incrementCount () {
      this.count++
    }
  }
}
</script>

 

テキストを入力すると、ログにcomputedとmethodsの両方が出力されます。
では、ボタンをクリックするとどうでしょう?

 

すると、computedは実行されず、methodsだけが実行されているのがわかります。
しかし、incrementCountを実行したのに、textの内容もログに表示されています。
このようにmethodsは依存関係にないプロパティが変更された場合でも実行されてしまうことが分かりました。

 

 


以上となります。
簡単に説明していきましたが、詳細は公式ドキュメントを参照してください。
では、またの機会に!


Top