はじめに
フロントエンジニアの齋藤です。Vueの開発をしていて、いまいちライフサイクルフックという概念について 曖昧な理解だったので調べてみました。
Vue.jsにおけるライフサイクルとは
コンポーネントには読み込まれる段階、レンダリングされる段階などのさまざまな状態があります。
他のコンポーネントを読み込んで表示される場合などはさらにそれぞれのコンポーネントがさまざまな状態を経ることになります。
そこでは、生成されて表示されるという変化だけではなく、不要になり非表示にされるという変化も考えられます。
このようなコンポーネントを解析、表示、非表示にする間に生じるさまざまなコンポーネントの状態の遷移をライフサイクルといいます。
またはVueインスタンスが生成されてから破棄されるまでの流れのことを言います。
そしてVueではライフサイクルのそれぞれの状態に応じて処理を行うライフサイクルフックという関数が用意されています。(コンポーネントの生成や更新、削除のタイミング)
ライフサイクルフックについて
ライフサイクルには主に以下のような種類があります。
beforeCreate →起動開始直後、Vueアプリケーションの初期化処理前
created →Vueアプリケーションの初期化処理後
mounted(最も使用頻度が高い) →DOMのレンダリングが完了し、表示状態になった状態
beforeUpdate →リアクティブデータが変更されて、DOMの再レンダリング処理を行う前
updated →DOMの再レンダリングが完了した時点
beforeUnmount →コンポーネントが破棄される直前
unmounted →コンポーネントが破棄されたあと
ライフサイクルフックの流れはこちらが参考になります。 https://ja.vuejs.org/guide/essentials/lifecycle.html#lifecycle-diagram
Options APIとComposition API
これはOptions APIとComposition APIによって記述の仕方が異なります。
記述の前にこの2つの方法について見ていきましょう
Options API
Options APIは、Vue.js 2.x以前から使用されていた伝統的なAPIです。
特徴としてはdata()でデータをまとめ、method()で使用されるファンクションを まとめてブロックごとでコードを分けていきます。
オプションオブジェクト自体がVueコンポーネントの構造を定義するため、関数をオプションオブジェクトから切り離すことが難しい一因です。
リアクティブな値にアクセスするために、thisを経由する必要があるためmethodsでの処理をComponentのViewから切り離すことができなくなり、冗長化された作りになります。
このthisを使用してコンポーネントのデータ、メソッド、算出プロパティ、ウォッチャー、および他のオプションにアクセスできます。
thisを使用することで、コンポーネント内のメソッドやライフサイクルフックが同じスコープ内で続けて定義されるため、大規模で複雑なコンポーネントではメンテナンスが難しくなります。
<script> export default { data() { return { count: '0', text: 'iimon', }; }, methods: { countUp() { this.count++; }, addText() { this.text += '$'; }, }, }; </script>
Composition API
Vue.js 3.xから導入された新しいAPIで、より柔軟で再利用可能なコードを可能にします。
setup
関数が導入され少ないコードでかけるようになり、コンポーネント外で関数を切り出すことができます。
これにより、関数ごとにコードを切り分けて再利用性を向上させることが可能です。これにより分割し管理を行うことで関連要素が分散せずに可読性の高いコンポーネント作成しやすくなりました。
this キーワードの代わりに、ref や reactive を使用してリアクティブなデータを宣言し、そのデータに直接アクセスします。
Options APIでは関連している変数やメソッドが離れてしまうことにより、視覚的にみづらくなってしまっていたがそれを解決しています。
大規模な構成になりそうならばこちらを使ったほうが良さそうです
<script> import { ref } from 'vue'; export default { setup(){ const count = ref(0); const countUp = () => { count.value++; } const text = ref('iimon'); const addText = () => { text.value += '$'; } return { count, countUp, text, addText }; }; }; </script>
ライフサイクルの記述の仕方
現在はvueのバージョンがvue2(Options API)からvue3(Composition API)に なりました。
beforeCreateやcreatedは高頻度で使用されていましたが、script setup構文 が主流のComposition APIでは、script setupのタイミングがbeforeCreateやcreatedに該当するためにこれら関数が不要になりました。
そのため、現在ライフサイクルフックでよく使用されるのがmounted、onMountedフックです。
mounted(onMounted)
やupdated(onUpdated)
についてもう一度詳しく見ていきましょう。
mounted(onMounted)
mounted
フックはコンポーネントが DOM に挿入された直後に実行されるライフサイクルフックです。このフックを使用することで、コンポーネントが DOM ツリーに正常にマウントされた後に実行するべき処理を記述できます。
vue2(Option API)ではmounted()
と記述し、
vue3(Composition API)ではimport文で読み込み、関数の中で処理をonMounted
実装します。
import {onMounted} from 'vue'; <script setup lang="ts"> onMounted(() => { console.log('Mounted: ' + message.value); }); }; </script>
updated(onUpdated)
DOMが更新されたときにonUpdatedイベントが発生します。これは通常、ページの読み込みが完了し、新しいデータやコンテンツが追加または変更されたときに発生します。
mounted(onMounted)と記述は同じです。
Vue2(Options API)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue Options API Example</title> </head> <body> <div id="app"></div> <!-- Vue.js CDN --> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script> <script> new Vue({ el: '#app', data() { return { message: 'Component has been mounted!', updateCount: 0, }; }, mounted() { console.log('Mounted: ' + this.message); }, updated() { console.log('Updated: ' + this.message + ' (Update Count: ' + this.updateCount + ')'); }, methods: { updateComponent() { this.updateCount++; this.message = 'Component has been updated!'; }, }, template: ` <div> <p>{{ message }}</p> <button @click="updateComponent">Update Component</button> </div> `, }); </script> </body> </html>
Vue3(Composition API)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue Composition API Example</title> </head> <body> <div id="app"></div> <!-- Vue.js CDN --> <script src="https://cdn.jsdelivr.net/npm/vue@3"></script> <script> const App = { setup() { const message = Vue.ref('Component has been mounted!'); const updateCount = Vue.ref(0); const updateComponent = () => { updateCount.value++; message.value = 'Component has been updated!'; }; Vue.onMounted(() => { console.log('Mounted: ' + message.value); }); Vue.onUpdated(() => { console.log('Updated: ' + message.value + ' (Update Count: ' + updateCount.value + ')'); }); return { message, updateComponent, }; }, template: ` <div> <p>{{ message }}</p> <button @click="updateComponent">Update Component</button> </div> `, }; const app = Vue.createApp(App); app.mount('#app'); </script> </body> </html>
cdnを使ってるからなのか、importがうまくできなかったのでここでは使用してません。
実際のコードでは、ページが表示された時にComponent has been mounted!
Update Componentボタンを押した時にComponent has been updated!
が表示されます。
まとめ
今まで曖昧だったライフサイクルについて学ぶことができたので、今まで上にライフサイクルを意識して開発しようと思います。