Help us understand the problem. What is going on with this article?

【Vue.js】ライフサイクルフック、イベント/キー修飾子のまとめ(コンポーネントもあるよ)

Vueまとめパート2

こちらの記事は、Adnan Babakan 氏によりDev.to上で公開された『 Vue cheat sheet 2 』の邦訳版です(原著者から許可を得た上での公開です)

原著をベースに説明の足りない部分は適宜、追記していく予定です。
(追記・改変の許可は得ています。)


Cover image for 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.$elmounted()が呼び出されるとドキュメント内要素になる。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-linkurlが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は使用しない。


  1. beforeMount()はサーバーサイドレンダリング(SSR)では呼び出されない。 

  2. mounted()はサーバーサイドレンダリング(SSR)では呼び出されない。 

  3. サーバーサイドでは最初1回のレンダリングのみ実行されるので、beforeUpdate()はサーバーサイドレンダリング(SSR)では呼び出されない。 

  4. updated()は、サーバーサイドレンダリング(SSR)では呼び出されない。 

  5. beforeDestroy()は、サーバーサイドレンダリング(SSR)では呼び出されない。 

  6. destroyed()は、サーバーサイドレンダリング(SSR)では呼び出されない。 

_masa_u
都内のスタートアップで働くプログラマーです。 仕事で学んだことを中心に備忘録として記事を書いています。 それ以外に海外の技術ブログの記事を邦訳したり、@baby-degu のメンバーの一員として記事選定や翻訳などのお手伝いをしています。
https://www.wantedly.com/users/42256816
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした