Vueまとめパート2
こちらの記事は、Adnan Babakan 氏によりDev.to上で公開された『 Vue cheat sheet 2 』の邦訳版です(原著者から許可を得た上での公開です)
原著をベースに説明の足りない部分は適宜、追記していく予定です。
(追記・改変の許可は得ています。)
DEV.toコミュニティのみなさん、こんにちは!
これは『Vueまとめ』シリーズのパート2です。
一部の説明とコードサンプルは、Vue.js公式ウェブサイトから引用しています。
チートシート
ライフサイクルフック
ライフサイクルフック | 実行されるタイミング |
---|---|
beforeCreate | Vueインスタンスが初期化された後(Vueインスタンスが作成される前) |
created | Vueインスタンスが作成された後 |
beforeMount | Vueインスタンスがマウントされる前 |
mounted | Vueインスタンスがマウントされた後 |
beforeUpdate |
data プロパティの値に変更があってDOMが更新される前 |
updated |
data プロパティの値に変更があってDOMが更新された後 |
beforeDestroy | Vueインスタンスが破棄される前 |
destroyed | Vueインスタンスが破棄された後 |
ライフサイクルフック(Lifecycle Hook)
ライフサイクルフックとは、特定のイベント/タイミングで実行されるVueコンポーネント内の関数のこと。
beforeCreate
beforeCreate()
はインスタンスが初期化された直後、データ(data
)監視とイベント/ウォッチャ(watch
)のセットアップの前に同期的に呼び出される。
const app = new Vue({
beforeCreate () {
console.log('Vueインスタンスが初期化されました!')
}
})
created
created()
はインスタンスの作成後に同期的に呼び出される。この段階では、インスタンスはオプション群の処理を完了している。つまり、データ(data
)監視、算出プロパティ(computed
)、メソッド(methods
)、ウォッチャ(watch
)、イベントコールバックのセットアップが完了している状態。ただし、マウントフェーズはまだ開始されておらず、$el
プロパティはまだ利用できない。
const app = new Vue({
created () {
console.log('Vueインスタンスが作成されました!')
}
})
beforeMount
マウントが始まる直前に呼び出される(レンダー関数が初めて呼び出される直前のタイミング)。1
const app = new Vue({
beforeMount () {
console.log('Vueインスタンスがマウントされようとしています!')
}
})
mounted
インスタンスがマウントされた後に呼び出され、el
は新しく作成されたvm.$el
に置き換えられる。ルートインスタンスがドキュメント内要素にマウントされている場合、vm.$el
はmounted()
が呼び出されるとドキュメント内要素になる。2
const app = new Vue({
mounted () {
console.log('Vueインスタンスがマウントされました!')
}
})
beforeUpdate
DOMにパッチが適用される前に、データ(data
)が変更されたときに呼び出される。これは、更新前に既存のDOMにアクセスするのに適したタイミングである。(たとえば、手動で追加されたイベントリスナーを削除する場合など)。3
const app = new Vue({
beforeUpdate() {
console.log('DOMが変更されようとしています!')
}
})
updated
updated()
はデータ変更によって仮想DOMが再レンダリングされてパッチが適用された後に呼び出される。
このフックが呼び出されると、コンポーネントのDOMが更新されるため、ここでDOMに依存する操作を実行できる。ただし、ほとんどの場合、このフック内で状態を変更すべきではない(無限更新ループになるため)。状態の変化に対応するには、代わりに算出プロパティ(computed
)かウォッチャ(watch
)を使うのが普通は良いとされる。4
const app = new Vue({
updated() {
console.log('DOMが更新されました!')
}
})
beforeDestroy
Vueインスタンスが破棄される直前に呼び出される。この段階では、インスタンスはまだ完全に機能している。5
const app = new Vue({
beforeDestroy() {
console.log('Vueインスタンスが破棄されようとしています!')
}
})
destroyed
destroyed()
はVueインスタンスが破棄された後に呼び出される。このフックが呼び出されると、Vueインスタンスのすべてのディレクティブがバインド解除され、すべてのイベントリスナーが削除され、すべての子Vueインスタンスも破棄される。6
const app = new Vue({
destroyed() {
console.log('Vueインスタンスが破棄されました!')
}
})
Events イベント
イベントは、HTMLにおける一つの要素おいて特定のアクションが実行されたときに呼び出される。
このシリーズの前の記事では、イベントについて基本的なことを説明したが、ここではもう少し詳しく説明する。
ちなみに@
はv-on
の省略形なのでお忘れなく。
利用可能なイベント
v-on
を使うと、すべてのJavaScriptイベントにアクセスできる。
<!-- ボタンに対して次のアクションが行われるとアラートが出る -->
<button @click="() => alert('Hello')">Do it</button>
<button @mouseover="() => alert('Hello')">Do it</button>
<button @mouseout="() => alert('Hello')">Do it</button>
<button @contextmenu="() => alert('Hello')">Do it</button>
フォームでsubmit
イベントを発生させることもできる。
<form @submit="() => alert('This form is submitted')">
<input type="text" />
</form>
イベント修飾子
イベント修飾子は、イベントの一部の動作を変更したり、イベントをより詳細に制御したりするために使用される。
イベント名の後の.
に続けて修飾子を書く。
/* イベント修飾子の使い方 */
v-on:<イベント名>.<イベント修飾子>
// または
@<イベント名>.<イベント修飾子>
// 修飾子をチェーンして書くこともできる(書いた順に実行される)
@<イベント名>.<イベント修飾子1>.<イベント修飾子2>
.stop
<!-- クリックイベントの伝搬が止まる -->
<a v-on:click.stop="doThis"></a>
.prevent
<!-- submit イベントによってページがリロードされない -->
<form v-on:submit.prevent="onSubmit"></form>
.capture
<!-- use capture mode when adding the event listener -->
<!-- i.e. an event targeting an inner element is handled here before being handled by that element -->
<div v-on:click.capture="doThis">...</div>
.once
.once
を使うと指定したクリックイベントは最大1回しかトリガーされない。
<!-- クリックイベントは1回だけ実行される -->
<a v-on:click.once="doThis"></a>
.self
<!-- event.target が要素自身のときだけ、ハンドラが呼び出される -->
<!-- 言い換えると子要素のときは呼び出されない -->
<div v-on:click.self="doThat">...</div>
.passive
<!-- `onScroll` が `event.preventDefault()` を含んでいたとしても -->
<!-- スクロールイベントのデフォルトの挙動(つまりスクロール)は -->
<!-- イベントの完了を待つことなくただちに発生するようになる -->
<div v-on:scroll.passive="onScroll">...</div>
キー修飾子/システム修飾子キー
Vueには、キーボードイベントを利用するときに、特定のキーをリスンするための修飾子が用意されている。
これらの修飾子は、keydown
(キーを押す)またはkeyup
(キーを押してから離す)などのいづれのキーイベントと一緒に使うことができる。
.enter
「enter」キーに反応をリスンする
<!-- Enterキーが押されたときにアラートする -->
<input @keydown.enter="() => alert('enterキーが押されました!')">
.tab
「tab」キーに反応をリスンする
<!-- Tabキーが押されたときにアラートする -->
<input @keydown.tab="() => alert('tabキーが押されました!')">
.delete
「delete」キーと「backspace」キーの反応をリスンする。
<!-- deleteキーorbackspaceが押されたときにアラートする -->
<input @keydown.delete="() => alert('deleteキーが押されました!')">
.esc
「esc」キーの反応をリスンする。
<!-- escキーが押されたときにアラートする -->
<input @keydown.esc="() => alert('espキーが押されました!')">
.space
「space」キーの反応をリスンする。
<!-- spaceキーが押されたときにアラートする -->
<input @keydown.space="() => alert('spaceキーが押されました!')">
.up
「↑カーソル」キーの反応をリスンする。
<!-- ↑カーソルキーが押されたときにアラートする -->
<input @keydown.up="() => alert('↑カーソルキーが押されました!')">
.down
「↓カーソル」キーの反応をリスンする。
<!-- ↓カーソルキーが押されたときにアラートする -->
<input @keydown.down="() => alert('↓カーソルキーが押されました!')">
.right
「→カーソル」キーの反応をリスンする。
<!-- →カーソルキーが押されたときにアラートする -->
<input @keydown.right="() => alert('→カーソルキーが押されました!')">
.left
「←カーソル」キーの反応をリスンする。
<!-- ←カーソルキーが押されたときにアラートする -->
<input @keydown.left="() => alert('←カーソルキーが押されました!')">
.home
「home」キーの反応をリスンする。
<!-- homeキーが押されたときにアラートする -->
<input @keydown.home="() => alert('homeキーが押されました!')">
.end
「end」キーの反応をリスンする。
<!-- endキーが押されたときにアラートする -->
<input @keydown.end="() => alert('endキーが押されました!')">
.ctrl
「ctrl」キーの反応をリスンする。
<!-- ctrlキーが押されたときにアラートする -->
<input @keydown.ctrl="() => alert('ctrlキーが押されました!')">
.alt
「alt」キーの反応をリスンする。
<!-- altキーが押されたときにアラートする -->
<input @keydown.alt="() => alert('altキーが押されました!')">
.shift
「shift」キーの反応をリスンする。
<!-- shiftキーが押されたときにアラートする -->
<input @keydown.shift="() => alert('shiftキーが押されました!')">
.meta
「meta」キーの反応をリスンする。
(Mac:コマンドキー(⌘)、Windows:ウィンドウキー(⊞))
<!-- metaキーが押されたときにアラートする -->
<input @keydown.meta="() => alert('metaキーが押されました!')">
Custom key code カスタムキーコード
Vueが必要なキー修飾子を提供しない場合は、次のようにそのキーコードを使うことができる。
<!--
キーコード「49」が押されたときにアラートする
(キーコード「49」は、キーボード上部の「1」。テンキーの「1」ではない)
-->
<input @keydown.49="() => alert('キーコード「49」キーが押されました!')">
キー修飾子の組み合わせ
次のように、複合キーに対応するためにキー修飾子をチェーンできる。
<input @keydown.ctrl.shift="() => alert('ctrlキーとshiftキーが同時に押されました!')">
.exact
exact
を使うと、必要なボタンだけを捕捉できる。他のボタンと一緒に使用した場合は捕捉されない。
exact
を使用しない場合、次のコードでShiftキーを押しながらCtrlキーを押すと、正常に応答する。
<!-- Shiftキー+Ctrlキーでもアラートが出る -->
<input @keydown.ctrl="() => alert('やあ!')">
ただし、以下のコードを使用すると、ctrl
だけを押さないと正常に応答しない。
<!-- ctrlキーだけを押す必要がある -->
<input @keydown.exact.ctrl="() => alert('やあ!')">
コンポーネント(Component)
コンポーネントは再利用可能なVueインスタンスであり、コードを簡素化・分割するために使用される。
次がVueコンポーネントの一例。
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
できる限り、テンプレートとそれに使用されるデータが埋め込まれていて、他のVueコンポーネント内で使用できるようにする。
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
これは、互いに何の関係もない3つの異なるボタンをレンダリングします。それぞれが個別に機能し、それぞれが独自のcount
データを持っている。(コンポーネントを定義する場合、各インスタンスが独自のデータを持たずデータが共有される場合を除いて、data
はオブジェクトを返す関数である必要がある。)
単一ファイルコンポーネント(Single File Component)
単一ファイルコンポーネント(またはSFC)は、1つのファイルとしてまとまっている1つのコンポーネントのこと(すごい!)。
次のようによりまとまった構造になっている。
<template>
<div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
コンポーネントの登録
コンポーネントを登録することにより、登録したコンポーネントをテンプレートで使用できる。
グローバルにコンポーネントを登録する
component()
を実行することにより、コンポーネントはすべてのVueインスタンスで利用可能になる(Vueインスタンスが複数ある場合):
import Vue from 'vue'
Vue.component('my-component', require('/path/to/your/component'))
インスタンスのスコープを制限して登録する
次のようにすることで、コンポーネントは指定されたVueインスタンスでのみ使用可能になる。
import Vue from 'vue'
const myComponent = require('/path/to/your/component')
const app = new Vue({
components: {
myComponent
}
})
遅延読み込みでコンポーネントを登録する
この方法は、コンポーネントをメインエントリファイルにバンドルしないため、非常に優れている。このため、Webサイトの読み込みが速くなり、必要なコンポーネントのみが必須になる。
import Vue from 'vue'
const myComponent = () => import('./components/myComponent ')
const app = new Vue({
components: {
myComponent
}
})
ついでに付け加えると、この登録方法では0などの番号で名前が付けられたコンポーネントが抽出される。webpackを使用している場合は、次のようにコンポーネントのファイル名を変更するためにマジックコメントを使用できる。
import Vue from 'vue'
const myComponent = () => import(/* webpackChunkName: "myComponent" */ './components/myComponent ')
let app = new Vue({
components: {
myComponent
}
})
props
コンポーネントは、HTMLタグの属性のように機能するpropsを持つことができます。
次のようなコンポーネントがあるとする。
Vue.component('blog-post', {
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
次のように使うことができる。
<blog-post post-title="hello!"></blog-post>
注:propsを定義する場合は、キャメルケースを使用して定義する方が適切ですが、使用する場合は、ケバブケースとして使用します。逆にVueでケバブケースで定義されたpropsをキャメルケースで使用することは可能だが、IDEがうまく認識できない可能性があるので注意。
単一ファイルコンポーネントには、propsを含めることもできる。
<template>
<div>
</div>
</template>
<script>
export default {
props: ['myProp'];
};
</script>
<style scoped>
</style>
slots
slotsは、コンポーネント内に他の要素またはコンポーネントを埋め込むために使用される。
シンプルなslot
単純なslotは名前のないslotであり、次のようにテンプレートで使用される。
<a :href="url" class="strange-class"><slot></slot></a>
コンポーネント名がnavigation-link
でurl
がpropと想定して上記のテンプレートを定義した後、次のように使うことができる。
<navigation-link url="/my-profile">Your profile</navigation-link>
見てのとおりコンポーネントの内部にテキストがあり、それは<slot></slot>
の代わりに挿入される。
名前付きslots
名前付きslotsは、名前なしスロットとは少し異なる。
次のbase-layout
というコンポーネントを定義する。
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
これを次のように使うことができる。
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
v-slot:slot-name
のついたテンプレートは、コンポーネントで定義されたそれぞれのスロットに配置され、その他は名前のない<slot></slot>
に配置される。
次のように名前のないslotには、実際にはdefault
という名前でアクセスできる。
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:default>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
</base-layout>
スコープ付きslots
slotはその親propにアクセス可能だが、避けたい場合は次のようにslot propを定義できる。
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
そして使用するときは次のようにする。
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
このスロットはそのコンポーネントで利用可能なuser
を使用し、親コンポーネントで利用可能なuser
は使用しない。