vue.jsのcreatedとmountedの違いを目で見て理解
vue.jsを含めフロントエンドの技術を学び始めるとライフサイクルという言葉を目にする機会が増えてきます。私がそうであったようにvue.jsを含めJavaScriptのフレームワークに触れたことがない人にとってはライフサイクルというものが一体何なのか理解するのが最初は非常に難しいと思います。最初はライフサイクルの中で行われているすべての処理を理解しようとせず利用頻度の高いcreated, mountedで何が行われるのかということと2つの区別がはっきりつけばライフサイクルの理解が進みます。本文書ではvue.jsのライフライクルフックcreatedとmountedの違いを要素elを使って説明していきます。要素elの説明は後ほど出てくるので安心してください。さらにVue3のOptions APIとComposition APIを利用してcreated, mountedの動作確認を行っています。
下記はvue.jsの公式サイトに掲載されているライフサイクル図です。今回はこの図の中でelとライフサイクルフックcreated, mountedさらにbeforeCreateの3つのみ注目します。
目次
ライフサイクルフックとは
created, mountedはライフサイクルフックと呼ばれ、vue.jsの初期化の中の決められたタイミングで実行される関数です。各ライフサイクルフックの中にプログラムを記述することでvue.jsの初期化の流れの中で記述したプログラムを必ず実行させることができます。
APIなどを利用して外部からデータを取得してブラウザに表示させたい場合は、ライフサイクルフックの中で外部リソースからデータ取得を行うことができるaxiosライブラリ、fetch関数を利用してデータ取得のプログラムを記述しておきます。ライフサイクルフックの中にデータ取得のプログラムを記述することでvue.jsの初期化中にデータの取得を開始し取得が完了するとブラウザにそのデータ内容を表示させることができます。
バージョンの確認
動作確認を行う前に現在利用しているvue.jsのバージョンを確認したい場合にはVue.versionで確認することができます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<body>
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
console.log(Vue.version);
//2.7.10
</script>
</body>
</body>
</html>
npmでインストールを行っている場合はpacakage.jsonファイルに記述されています。
{
//略
"dependencies": {
"vue": "^3.2.37"
},
"devDependencies": {
//略
}
}
動作確認を行う際にVue2とVue3では異なるcdnを利用しています。cdnが利用できない場合もあるのでその時はVueの各バージョンのドキュメントを確認してください。Vue2はhttps://v2.vuejs.org/ , Vue3はhttps://vuejs.org/です。
//Vue2
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js></script>
//Vue3
<script src="https://unpkg.com/vue@3"></script>
elがなにか確認してみよう
Vue.jsのバージョン2を利用している場合
ライフサイクルを記述した上のライフサイクル図の中に”el”という文字列を見つけることができます。elはvue.jsインスタンスがマウントを行う要素で、elを指定した要素の中でだけvue.jsを動かすことができます。Vueをインスタンス化する際にnew Vueで下記のようにしてelプロパティに#appといった要素を特定するidの値を記述します。
var app = new Vue({
el: '#app',
data: {
},
})
このelが一体どのような情報を持っているのか確認してみましょう。elにはthis.$elでアクセスすることができます。thisはvueのインスタンスを表します。
button要素を作ってv-onディレクティブでclickイベントを設定し、クリックが行われたらshowElメソッドを実行しコンソールにthis.$elの中身を表示させます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LifeCycle Hooks</title>
</head>
<body>
<div id="app">
<h1>Hello World</h1>
<button v-on:click="showEl">show el</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {},
methods: {
showEl: function () {
console.log(this.$el);
},
},
});
</script>
</body>
</html>
デベロッパーツールのコンソールに<div id=”#app”></div>が表示されます。中身は、document.getElementById(‘app’)を使って取得できるものと同じです。
getElementById(‘app’)で取得できる要素と同じなので、showElメソッドのconsole.logの前に下記のコードを追加するとinnnerHTMLで中身を書き換えることも可能です。buttonをクリックするとHello WorldがHello Vueに書き換えられることが確認できます。
this.$el.innerHTML = "<h1>Hello Vue</h1>";
ここまでの動作確認でelはnew Vueインスタンスで指定した要素そのものであることがわかりました。
Vue.jsのバージョン3を利用している場合
本文書ではcdnにVue.jsのバージョン2を利用しています。Vue.jsのバージョン3ではVueインスタンスの作成方法が変更になっています。Vue.jsのバージョン3を利用した場合にthis.$elに何が表示されるのか確認します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LifeCycle Hooks</title>
</head>
<body>
<div id="app">
<h1>Hello World</h1>
<button v-on:click="showEl">show el</button>
</div>
<script src="https://unpkg.com/vue@3"></script>
<script>
Vue.createApp({
methods: {
showEl() {
console.log(this.$el);
},
},
}).mount('#app');
</script>
</body>
</html>
デベロッパーツールで確認するとappの要素ではなくtextと表示されます。表示される内容バージョン2, バージョン3では大きく異なっていることが確認できます。
createdとmountedの違い
Vue2の場合
elの中身がわかったので、ライフサイクルフックのcreatedとmountedの違いを確認しておきましょう。createdとmountedの違いは下記のように説明されているのをよく見かけます。
createdはDOMがまだ作られていない状態で、mountedではDOMが作成された直後の状態です。
DOMの作成、未作成でライフサイクルフックに違いがあるということなので、this.$elを使ってその違いを確認してみましょう。確認方法はシンプルでcreatedとmountedにconsole.logでthis.$elがそれぞれの状態でどのようになっているか確認するだけです。
this.$elはDOMの要素なのでDOMが作成されていないとthis.$elは存在しません。
new Vue({
el: '#app',
data: {
},
created : function(){
console.log('created')
console.log(this.$el)
},
mounted : function(){
console.log('mounted')
console.log(this.$el)
}
})
createdではDOMが作成されていないので、this.$elはundefinedとなっており、mountedではDOMの作成が完成しているので<div id=”app”></div>が表示されます。
これでcreated, mountedの違いがわかりました。そのため、createdの時にgetElementById(‘id’)を使ってDOMの要素を取得しようとしても取得することはできません。しかしmounted時にはDOMの作成が完了しているのでDOMの要素が取得できるためgetElementByIdだけではなくDOMに対してアクセスを行うvue.js以外のライブラリjQuery等もこの時点で使用可能となります。
Vue3の場合
Vue3の場合もcreated, mountedの違いをコンソールで確認しておきましょう。
Vue.createApp({
methods: {
showEl() {
console.log(this.$el);
},
},
created: function () {
console.log('created');
console.log(this.$el);
},
mounted: function () {
console.log('mounted');
console.log(this.$el);
},
}).mount('#app');
Vue2では異なりcreated時のthis.$elはundefinedではなくnullになっていることが確認できます。しかし#textと表示されている通りmountedでは$elにアクセスできていることがわかります。
createdでは何が完了しているのか
Vue2の場合
ではcreatedでは何が完了しているのでしょう。先程まではthis.$elでelの状態を確認しましたが、その中身は見えませんでした。今度はthisを使ってVueインスタンスを確認します。今回はdataにmessageプロパティの設定も行っておきます。
new Vue({
el: '#app',
data: {
message : 'Hello World'
},
created : function(){
this.message = 'Hello Vue'
console.log('created')
console.log(this)
},
});
createdではVueインスタンスの作成が完了しているので、設定しているmessageをVueの中で確認することができます。this.messageをcreatedの中で上書きした値も反映されていることが確認できます。これでcreatedでインスタンスの作成が完了、dataオブジェクトがリアクティブになっているという説明を受けても理解することができます。
dataオブジェクトがリアクティブになっているので下記のように設定を行うとブラウザ上には”Hello Vue”が表示されます。
<div id="app">
<h1>{{message}}</h1>
</div>
dataオブジェクトがリアクティブになっているのでcreatedの中でAPIを使ってデータ取得を開始しても取得したデータを設定することができるのでcreatedの中で外部からデータを取得しても問題がないことがわかるかと思います。このことが理解できた上で下記のようなコードを見ても納得できるかと思います。createdの中でデータプロパティusersに外部サーバから取得したデータを挿入しています。
created :function(){
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => this.users = response.data)
}
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LifeCycle Hooks</title>
</head>
<body>
<div id="app">
<h1>Hello World</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
users: [],
},
created: function () {
axios
.get('https://jsonplaceholder.typicode.com/users')
.then((response) => (this.users = response.data));
},
});
</script>
</body>
</html>
axiosではなくfetch関数を利用した場合は以下のように記述することができます。
async created() {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await response.json();
console.log(users)
},
async, awaitをまだ学習していない人であれば下記のように記述も可能です。
created() {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((data) => console.log(data));
},
Vue 3の場合
Vue3の場合はcreatedの中でmessageプロパティの値が更新できるか確認しておきます。
Vue.createApp({
data() {
return {
message: 'Hello World',
};
},
methods: {
showEl() {
console.log(this.$el);
},
},
created() {
console.log('created');
this.message = 'Hello Vue';
console.log(this);
},
}).mount('#app');
下記のようにcreatedで確認しているthisの表示は変わっていますがmessageを見るとHello WorldからHello Vueに更新されていることがわかります。
createdとbeforeCreateの違い
beforeCreateでは、dataプロパティの中身を確認することができますがcreatedのようにmessageの上書きを行うことはできません。dataプロパティに関係のない処理を行う際に利用することができます。
beforeCreate : function(){
this.message = 'Hello Vue'
console.log('beforeCreate')
console.log(this)
}
これでbeforeCreateでインスタンスの作成が完了、dataオブジェクトがリアクティブになっていないという説明を受けても理解することができます。
ここまでの説明を読み終えるとelの理解、beforeCreate, created, mountedの3つのライフサイクルフックの違いをしっかり理解できたかと思います。ライフサイクル図には他にもさまざまなライフサイクルフックが記述されていました。今回は3つですが、それぞれのフックで行われることが異なるので違いを調べるとよりVue.jsの理解が深まると思います。
Options API vs Composition API
Vue3にはこれまでの記述方法のOptions APIの他に新たにComposition APIで記述することができます。Composition APIではcreatedなど一部のライフサイクルフックがなくなっています。Composition APIでのmounted Hookの記述方法はなくなっているのでcreated Hookがどうなったかについて確認を行なっていきます。
Options APIでcreated, mounted
先程確認を行っていますが再度Options APIを利用してcreated, mountedの動作確認を行います。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LifeCycle Hooks</title>
</head>
<body>
<div id="app">
<h1>Hello World</h1>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
created() {
console.log('created');
},
mounted() {
console.log('mounted');
},
}).mount('#app');
</script>
</body>
</html>
デベロッパーツールのコンソールを確認するとcreated, mountedが表示されます。
createdの中でdataで定義したmessageプロパティが更新されることも確認しておきます。messageプロパティの初期値は’Hello World’ですが、createdの中で’Hello Vue’に上書きしています。createdの中でmessageプロパティにアクセスすることができるためブラウザ上には更新されたHello Vueが表示されます。
<template>
<h1>{{ message }}</h1>
</template>
<script>
export default {
data() {
return {
message: 'Hello World',
};
},
created() {
this.message = 'Hello Vue';
console.log(this.message);
},
};
</script>
Composition APIのmounted
Composition APIではmountedではなくonMountedに名前が変わっている上createdは無くなっています。Options APIとは記述方法が異なるのでComposition APIでonMountedの動作確認を行います。
Vue.createApp({
setup() {
Vue.onMounted(() => {
console.log('mounted');
});
},
}).mount('#app');
デベロッパーツールにはmountedが表示されます。createdはなくなりましたがその代わりとなる方法はあるのでしょうか。Composition APIではcreated Hookはなくなりましたがsetup関数の中にそのまま処理を記述することでcrteatedと同じことができます。
Vue.createApp({
setup() {
console.log('created');
Vue.onMounted(() => {
console.log('mounted');
});
},
}).mount('#app');
コンソールにはcreatedが表示されました。createdが表示されるだけではcreatedと同じことができるかはわからないのでリアクティブなデータmessageをrefで定義して更新できるか確認します。
<body>
<div id="app">
<h1>{{ message }}</h1>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
setup() {
const message = Vue.ref('Hello World');
message.value = 'Hello Vue';
return {
message,
};
},
}).mount('#app');
</script>
</body>
動作確認を行うとブラウザにHello Vueが表示されることが確認できます。
setup関数の中でfetch関数を実行して情報が取得できるかも確認しておきます。
<body>
<div id="app">
<h1>Hello World</h1>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
setup() {
const users = Vue.ref([]);
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((data) => (users.value = data));
return {
users,
};
},
}).mount('#app');
</script>
</body>
ブラウザ上にはユーザの一覧が表示されます。Composition APIではcreatedがなくなりましたがsetup関数の中で実行することでOptions APIではcreatedの中で実行していたことができることが確認できました。
Composion APIではbeforeCreateに対応する処理はないのでcreatedと同様にsetup関数に直接記述することになります。