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

# Vueまとめパート2

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

- 元記事：[Vue cheat sheet 2 ](https://dev.to/adnanbabakan/vue-cheat-sheet-2-5aj8)

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

---

![Cover image for Vue cheat sheet 2](https://res.cloudinary.com/practicaldev/image/fetch/s--75klwUpK--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://thepracticaldev.s3.amazonaws.com/i/wzfiefy845m14ufg9j5l.png)


DEV.toコミュニティのみなさん、こんにちは！

これは『Vueまとめ』シリーズのパート2です。

一部の説明とコードサンプルは、[Vue.js公式ウェブサイト](https://jp.vuejs.org/v2/guide/)から引用しています。

## チートシート

### ライフサイクルフック

|ライフサイクルフック|実行されるタイミング|
|:-----------------:|:------------------:|
|[beforeCreate](#beforecreate)|Vueインスタンスが初期化された後（Vueインスタンスが作成される前）|
|[created](#created)|Vueインスタンスが作成された後|
|[beforeMount](#beforemount)|Vueインスタンスがマウントされる前|
|[mounted](#mounted)|Vueインスタンスがマウントされた後|
|[beforeUpdate](#beforeupdate) |`data`プロパティの値に変更があってDOMが更新される前|
|[updated](#updated)|`data`プロパティの値に変更があってDOMが更新された後|
|[beforeDestroy](#beforedestroy)|Vueインスタンスが破棄される前|
|[destroyed](#destroyed)|Vueインスタンスが破棄された後|

<!-- 

### イベント修飾子 
- [`.stop`](#`.stop`)
- [`.prevent`](#`.prevent`)
- [`.capture`](#`.capture`)
- [`.once`](#`.once`)
- [`.self`](#`.self`)
- [`.passive`](#`.passive`)

|ライフサイクルフック|実行されるタイミング|
|:-----------------:|:------------------:|
|[`.stop`](#`.stop`)|Vueインスタンスが初期化された後（Vueインスタンスが作成される前）|
|[`created`](#`created`)|Vueインスタンスが作成された後|
|[`beforeMount`](#`beforeMount`)|Vueインスタンスがマウントされる前|
|[`mounted`](#`Mounted`)|Vueインスタンスがマウントされた後|
|[`beforeUpdate`](#`beforeUpdate`) |`data`プロパティの値に変更があってDOMが更新される前|
|[`updated`](#`updated`)|`data`プロパティの値に変更があってDOMが更新された後|
|[`beforeDestroy`](#`beforeDestroy`)|Vueインスタンスが破棄される前|
|[`destroyed`](#`destroyed`)|Vueインスタンスが破棄された後|



### キー修飾子/イベント修飾子
- [`.enter`](#`.enter`)
- [`.tab`](#`.tab`)
- [`.delete`](#`.delete`)
- [`.esc`](#`.esc`)
- [`.space`](#`.space`)
- [`.up`](#`.up`)
- [`.down`](#`.down`)
- [`.right`](#`.right`)
- [`.left`](#`.left`)
- [`.home`](#`.home`)
- [`.end`](#`.end`)
- [`.ctrl`](#`.ctrl`)
- [`.alt`](#`.alt`)
- [`.shift`](#`.shift`)
- [`.meta`](#`.meta`)
- [カスタムキー修飾子](#カスタムキー修飾子)

### コンポーネント

*   [Components](#components)
    *   [Single file component](#single-file-component)
    *   [Registering components](#registering-components)
        *   [Global component registration](#global-component-registration)
        *   [Instance scoped component registration](#instance-scoped-component%20-registration)
        *   [Lazy load component registration](#lazy-load-component-registration)
    *   [Component props](#component-props)
    *   [Component slots](#component-slots)
        *   [Simple slot](#simple-slot)
        *   [Named slots](#named-slots)
        *   [Scoped slots](#scoped-slots)

-->


## ライフサイクルフック（Lifecycle Hook）

ライフサイクルフックとは、特定のイベント/タイミングで実行されるVueコンポーネント内の関数のこと。

### beforeCreate

`beforeCreate()`はインスタンスが初期化された直後、データ（`data`）監視とイベント/ウォッチャ（`watch`）のセットアップの前に同期的に呼び出される。

```javascript
const app = new Vue({
    beforeCreate () {
        console.log('Vueインスタンスが初期化されました！')
    }
})
``` 

### created

`created()`はインスタンスの作成後に同期的に呼び出される。この段階では、インスタンスはオプション群の処理を完了している。つまり、データ（`data`）監視、算出プロパティ（`computed`）、メソッド（`methods`）、ウォッチャ（`watch`）、イベントコールバックのセットアップが完了している状態。ただし、マウントフェーズはまだ開始されておらず、`$el`プロパティはまだ利用できない。


```javascript

const app = new Vue({
    created () {
        console.log('Vueインスタンスが作成されました！')
    }
})
```


### beforeMount

マウントが始まる直前に呼び出される（レンダー関数が初めて呼び出される直前のタイミング）。[^1]

[^1]: `beforeMount()`はサーバーサイドレンダリング（SSR）では呼び出されない。


```javascript
const app = new Vue({
    beforeMount () {
        console.log('Vueインスタンスがマウントされようとしています!')
    }
})
```    

### mounted

<!-- 
Called after the instance has been mounted, where `el` is replaced by the newly created `vm.$el`. If the root instance is mounted to an in-document element, `vm.$el` will also be in-document when mounted is called.

-->

インスタンスがマウントされた後に呼び出され、`el`は新しく作成された`vm.$el`に置き換えられる。ルートインスタンスがドキュメント内要素にマウントされている場合、`vm.$el`は`mounted()`が呼び出されるとドキュメント内要素になる。[^2]

[^2]: `mounted()`はサーバーサイドレンダリング（SSR）では呼び出されない。


```javascript

const app = new Vue({
    mounted () {
        console.log('Vueインスタンスがマウントされました！')
    }
})
```    

### beforeUpdate

DOMにパッチが適用される前に、データ（`data`）が変更されたときに呼び出される。これは、更新前に既存のDOMにアクセスするのに適したタイミングである。（たとえば、手動で追加されたイベントリスナーを削除する場合など）。[^3]

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


```javascript
const app = new Vue({
    beforeUpdate() {
        console.log('DOMが変更されようとしています！')
    }
})
```

    
### updated


`updated()`はデータ変更によって仮想DOMが再レンダリングされてパッチが適用された後に呼び出される。

このフックが呼び出されると、コンポーネントのDOMが更新されるため、ここでDOMに依存する操作を実行できる。ただし、ほとんどの場合、このフック内で状態を変更すべきではない（無限更新ループになるため）。状態の変化に対応するには、代わりに算出プロパティ（`computed`）かウォッチャ（`watch`）を使うのが普通は良いとされる。[^4]

[^4]: `updated()`は、サーバーサイドレンダリング（SSR）では呼び出されない。


```javascript

const app = new Vue({
    updated() {
         console.log('DOMが更新されました！')
    }
})
```

### beforeDestroy

Vueインスタンスが破棄される直前に呼び出される。この段階では、インスタンスはまだ完全に機能している。[^5]

[^5]: `beforeDestroy()`は、サーバーサイドレンダリング（SSR）では呼び出されない。



```javascript

const app = new Vue({
    beforeDestroy() {
        console.log('Vueインスタンスが破棄されようとしています！')
    }
})
```    

### destroyed

`destroyed()`はVueインスタンスが破棄された後に呼び出される。このフックが呼び出されると、Vueインスタンスのすべてのディレクティブがバインド解除され、すべてのイベントリスナーが削除され、すべての子Vueインスタンスも破棄される。[^6]

[^6]: `destroyed()`は、サーバーサイドレンダリング（SSR）では呼び出されない。


```javascript
const app = new Vue({
    destroyed() {
        console.log('Vueインスタンスが破棄されました！')
    }
})

```

## Events イベント

イベントは、HTMLにおける一つの要素おいて特定のアクションが実行されたときに呼び出される。

このシリーズの前の記事では、イベントについて基本的なことを説明したが、ここではもう少し詳しく説明する。

ちなみに`@`は`v-on`の省略形なのでお忘れなく。

### 利用可能なイベント

`v-on`を使うと、すべてのJavaScriptイベントにアクセスできる。

```vue
<!-- ボタンに対して次のアクションが行われるとアラートが出る -->
<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`イベントを発生させることもできる。

```vue

<form @submit="() => alert('This form is submitted')">
  <input type="text" />
</form>
```

### イベント修飾子


イベント修飾子は、イベントの一部の動作を変更したり、イベントをより詳細に制御したりするために使用される。

イベント名の後の`.`に続けて修飾子を書く。

```javascript
/* イベント修飾子の使い方 */
v-on:<イベント名>.<イベント修飾子>
// または
@<イベント名>.<イベント修飾子>
// 修飾子をチェーンして書くこともできる（書いた順に実行される）
@<イベント名>.<イベント修飾子1>.<イベント修飾子2>
```

#### `.stop`

```vue
<!-- クリックイベントの伝搬が止まる -->
<a v-on:click.stop="doThis"></a>
```    

#### `.prevent`

```vue
<!-- submit イベントによってページがリロードされない -->
<form v-on:submit.prevent="onSubmit"></form>
```

#### `.capture`



```vue
<!-- 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回しかトリガーされない。

```vue
<!-- クリックイベントは1回だけ実行される -->
<a v-on:click.once="doThis"></a>
```    

#### `.self`


```vue
<!-- event.target が要素自身のときだけ、ハンドラが呼び出される -->
<!-- 言い換えると子要素のときは呼び出されない -->
<div v-on:click.self="doThat">...</div>
```    

#### `.passive`

```vue
<!-- `onScroll` が `event.preventDefault()` を含んでいたとしても -->
<!-- スクロールイベントのデフォルトの挙動(つまりスクロール)は -->
<!-- イベントの完了を待つことなくただちに発生するようになる -->
<div v-on:scroll.passive="onScroll">...</div>
```    

### キー修飾子/システム修飾子キー


Vueには、キーボードイベントを利用するときに、特定のキーをリスンするための修飾子が用意されている。

これらの修飾子は、`keydown`（キーを押す）または`keyup`（キーを押してから離す）などのいづれのキーイベントと一緒に使うことができる。


#### `.enter`
「enter」キーに反応をリスンする

```vue
<!-- Enterキーが押されたときにアラートする -->
<input @keydown.enter="() => alert('enterキーが押されました！')">
```

#### `.tab`
「tab」キーに反応をリスンする


```vue
<!-- Tabキーが押されたときにアラートする  -->
<input @keydown.tab="() => alert('tabキーが押されました！')">
```


#### `.delete`

「delete」キーと「backspace」キーの反応をリスンする。

```vue
<!-- deleteキーorbackspaceが押されたときにアラートする -->
<input @keydown.delete="() => alert('deleteキーが押されました！')">
```    

#### `.esc`

「esc」キーの反応をリスンする。

```vue
<!-- escキーが押されたときにアラートする -->
<input @keydown.esc="() => alert('espキーが押されました！')">
```

#### `.space`

「space」キーの反応をリスンする。

```vue
<!-- spaceキーが押されたときにアラートする -->
<input @keydown.space="() => alert('spaceキーが押されました！')">
```
    

#### `.up`

「↑カーソル」キーの反応をリスンする。


```vue
<!-- ↑カーソルキーが押されたときにアラートする -->
<input @keydown.up="() => alert('↑カーソルキーが押されました！')">
```

#### `.down`

「↓カーソル」キーの反応をリスンする。


```vue
<!-- ↓カーソルキーが押されたときにアラートする -->
<input @keydown.down="() => alert('↓カーソルキーが押されました！')">
```


#### `.right`

「→カーソル」キーの反応をリスンする。

```vue
<!-- →カーソルキーが押されたときにアラートする -->
<input @keydown.right="() => alert('→カーソルキーが押されました！')">
```

#### `.left`
「←カーソル」キーの反応をリスンする。

```vue
<!-- ←カーソルキーが押されたときにアラートする -->
<input @keydown.left="() => alert('←カーソルキーが押されました！')">
```

#### `.home`

「home」キーの反応をリスンする。

```vue
<!-- homeキーが押されたときにアラートする -->
<input @keydown.home="() => alert('homeキーが押されました！')">
```    

#### `.end`

「end」キーの反応をリスンする。

```vue
<!-- endキーが押されたときにアラートする -->
<input @keydown.end="() => alert('endキーが押されました！')">
```

#### `.ctrl`

「ctrl」キーの反応をリスンする。

```vue
<!-- ctrlキーが押されたときにアラートする -->
<input @keydown.ctrl="() => alert('ctrlキーが押されました！')">
```

#### `.alt`

「alt」キーの反応をリスンする。

```vue
<!-- altキーが押されたときにアラートする -->
<input @keydown.alt="() => alert('altキーが押されました！')">
```

#### `.shift`

「shift」キーの反応をリスンする。


```vue
<!-- shiftキーが押されたときにアラートする -->
<input @keydown.shift="() => alert('shiftキーが押されました！')">
```


#### `.meta`

「meta」キーの反応をリスンする。
（Mac：コマンドキー（⌘）、Windows：ウィンドウキー（⊞））

```vue
<!-- metaキーが押されたときにアラートする -->
<input @keydown.meta="() => alert('metaキーが押されました！')">
```


#### Custom key code カスタムキーコード

Vueが必要なキー修飾子を提供しない場合は、次のようにそのキーコードを使うことができる。

```vue
<!-- 
キーコード「49」が押されたときにアラートする
（キーコード「49」は、キーボード上部の「1」。テンキーの「1」ではない）
-->
<input @keydown.49="() => alert('キーコード「49」キーが押されました！')">
```

### キー修飾子の組み合わせ

次のように、複合キーに対応するためにキー修飾子をチェーンできる。

```vue
<input @keydown.ctrl.shift="() => alert('ctrlキーとshiftキーが同時に押されました！')">
```

### `.exact`

`exact`を使うと、必要なボタンだけを捕捉できる。他のボタンと一緒に使用した場合は捕捉されない。

`exact`を使用しない場合、次のコードでShiftキーを押しながらCtrlキーを押すと、正常に応答する。

```vue
<!-- Shiftキー＋Ctrlキーでもアラートが出る -->
<input @keydown.ctrl="() => alert('やあ！')">
```

ただし、以下のコードを使用すると、`ctrl`だけを押さないと正常に応答しない。

```vue
<!-- ctrlキーだけを押す必要がある -->
<input @keydown.exact.ctrl="() => alert('やあ！')">
```

## コンポーネント（Component）

コンポーネントは再利用可能なVueインスタンスであり、コードを簡素化・分割するために使用される。

次がVueコンポーネントの一例。

```javascript

Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
```

できる限り、テンプレートとそれに使用されるデータが埋め込まれていて、他のVueコンポーネント内で使用できるようにする。

```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）は、１つのファイルとしてまとまっている１つのコンポーネントのこと（すごい！）。

次のようによりまとまった構造になっている。  

```vue
<template>
    <div>

    </div>
</template>

<script>
    export default {

    }
</script>

<style scoped>

</style>
```

### コンポーネントの登録

コンポーネントを登録することにより、登録したコンポーネントをテンプレートで使用できる。

#### グローバルにコンポーネントを登録する

`component()`を実行することにより、コンポーネントはすべてのVueインスタンスで利用可能になる（Vueインスタンスが複数ある場合）：

```javascript
import Vue from 'vue'

Vue.component('my-component', require('/path/to/your/component'))
```
    

#### インスタンスのスコープを制限して登録する

次のようにすることで、コンポーネントは指定されたVueインスタンスでのみ使用可能になる。

```javascript

import Vue from 'vue'

const myComponent = require('/path/to/your/component')

const app = new Vue({
    components: {
        myComponent
    }
})
```
    

#### 遅延読み込みでコンポーネントを登録する

この方法は、コンポーネントをメインエントリファイルにバンドルしないため、非常に優れている。このため、Webサイトの読み込みが速くなり、必要なコンポーネントのみが必須になる。

```javascript
import Vue from 'vue'

const myComponent = () => import('./components/myComponent ')

const app = new Vue({
    components: {
        myComponent 
    }
})
```    

ついでに付け加えると、この登録方法では0などの番号で名前が付けられたコンポーネントが抽出される。webpackを使用している場合は、次のようにコンポーネントのファイル名を変更するためにマジックコメントを使用できる。

```javascript

import Vue from 'vue'

const myComponent = () => import(/* webpackChunkName: "myComponent" */ './components/myComponent ')

let app = new Vue({
    components: {
        myComponent 
    }
})
```
    

### props

コンポーネントは、HTMLタグの属性のように機能するpropsを持つことができます。

次のようなコンポーネントがあるとする。

```vue
Vue.component('blog-post', {
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
```

次のように使うことができる。

```vue
<blog-post post-title="hello!"></blog-post>
```

注：propsを定義する場合は、キャメルケースを使用して定義する方が適切ですが、使用する場合は、ケバブケースとして使用します。逆にVueでケバブケースで定義されたpropsをキャメルケースで使用することは可能だが、IDEがうまく認識できない可能性があるので注意。

単一ファイルコンポーネントには、propsを含めることもできる。

```vue
<template>
    <div>

    </div>
</template>

<script>
    export default {
           props: ['myProp'];
    };
</script>

<style scoped>

</style>
```

### slots

slotsは、コンポーネント内に他の要素またはコンポーネントを埋め込むために使用される。

#### シンプルなslot

単純なslotは名前のないslotであり、次のようにテンプレートで使用される。 

```vue
<a :href="url" class="strange-class"><slot></slot></a>
```

コンポーネント名が`navigation-link`で`url`がpropと想定して上記のテンプレートを定義した後、次のように使うことができる。

```vue
<navigation-link url="/my-profile">Your profile</navigation-link>
```

見てのとおりコンポーネントの内部にテキストがあり、それは`<slot></slot>`の代わりに挿入される。

#### 名前付きslots

名前付きslotsは、名前なしスロットとは少し異なる。

次の`base-layout`というコンポーネントを定義する。

```vue
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
```

これを次のように使うことができる。

```vue
<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`という名前でアクセスできる。**

```vue
<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を定義できる。

```vue
<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>
```
 
そして使用するときは次のようにする。

```vue
<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>
```

このスロットはそのコンポーネントで利用可能な`user`を使用し、親コンポーネントで利用可能な`user`は使用しない。