Vue開発者のためのVue.jsベストプラクティス集15選

# はじめに

**みなさん、Vue使ってますかー！**・・・・・・・（へんじがない。ただのしかばねのようだ。）

私は毎日使ってます。が、正しい使い方というのがいまいちわかっていません。ということで、ネットで拾ってきた情報を元に**Vueで開発する上でのベストプラクティス集15選をまとめてみました。**

「ふんふんそうだよね〜」といったものから「えー！？そうなのー！？」となるものまで集めています。皆さんの開発の手助けになる情報が入っていると幸いです。

また、間違っている内容やさらに良い方法等あれば、コメント欄にて（優しく）ご指摘いただけるととても嬉しいです。


# 1. `v-for`内では必ず`:key`を使う
``v-for``[ディレクティブ](https://012-jp.vuejs.org/api/directives.html)でkey属性を使うと、**データを操作するのに役立ちます。**さらに、Vueがコンポーネントの状態を追跡し、それぞれのエレメントに対し**継続的に参照ができるようになります。** 特に、アニメーションやVue[トランジション](https://v1-jp.vuejs.org/guide/transitions.html)でkeyが活躍するので興味ある方はリンク先をみてみてください。

keyがない場合、VueはDOMを効率よく生成する「だけ」になります。さらに``v-for``内のエレメントが順番通り表示されないといった、振る舞いが予測できなくなる等の弊害が発生します。

上記の理由から、``v-for``を使うときは必ず`key`もセットしましょう。それぞれのエレメントに固有のキーを割り振ることで、VueアプリがDOM操作をどの様に行うか、予想が簡単になります。

```vue
<!-- 悪い例 -->
<div v-for='animal in animals' />

<!-- 良い例 -->
<div v-for='animal in animals' :key='animal.id' />
```

# 2. イベント名にはケバブケースを使う 
カスタムイベントをemitさせる際のイベント名は、ケバブケース(kebab-case)が最適です。親コンポーネントで同じ構文のイベントを動かすからです。

イベント名はVueが自動的に小文字に変換してくれますが、コンポーネント間で一貫性を保ち読みやすいコードにするために、ケバブケースを使うことをお勧めします。

```javascript
// 子コンポーネント
this.$emit('close-window')
```

```vuejs
<!-- 親コンポーネント -->
<popup-window v-on:close-window='handleEvent()' />
```

# 3. Propsの宣言にはキャメルケース、テンプレートにはケバブケースを使う
JavaScriptではキャメルケース(camelCase)が基本的ですが、HTMLではケバブケース(Kebab case)を使います。Vueでもこのルールに従って命名しましょう。

Vueはケバブケースとキャメルケースを自動で変換してくれるので、宣言する時以外はあまり気にする必要はありません。

```vuejs

<!-- 悪い例 -->
<PopupWindow greetingText='good morning!' /> 
props: { 'greeting-text': String }

<!-- 良い例 -->
<PopupWindow greeting-text='good morning!' /> 
props: { greetingText: String }
```

# 4. コンポーネントオプションの順番はスタイルガイドに従う
コンポーネントのオプションは同じ順番で定義しましょう。探す時間が省けたり、新しくコンポーネントを作る際に便利です。

例えば、イベントの順番は下記のように定義すると良しとされています。
`watch`
`beforeCreate`
`created`
`beforeMount`
`mounted`
`beforeUpdate`
`updated`
`activated`
`deactivated`
`beforeDestroy`
`destroyed`

 **[公式ドキュメント(コンポーネント/インスタンス オプション順序)](https://jp.vuejs.org/v2/style-guide/index.html#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88-%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9-%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E9%A0%86%E5%BA%8F-%E6%8E%A8%E5%A5%A8)** に詳しくのっているので、ぜひ一読してみてください。

# 5. データプロパティでは必ずFunctionを返す
コンポーネントでデータを宣言する際、データプロパティには必ずfunctionをreturnするようにしましょう。returnしない場合、オブジェクトを返すことになり、**データがすべてのコンポーネントのインスタンスで共有されてしまいます。**

再利用可能で固有のオブジェクトを返すコンポーネントを作る場合がほとんどだと思いますが、データオブジェクトをfunction内でreturnすることで実現することができます。

```javascript
// 悪い例 
data: {
  name: 'Momoko Toyota',
  hobbies: []
}

// 良い例
data () {
  return {
    name: 'Momoko Toyota',
    hobbies: []
  }
}
```

# 6. `v-for`と`v-if`は同じエレメントで使わない
エレメントを何らかの条件でフィルターする時、``v-if``と``v-for``を一緒に使いたくなりますよね。

```html
<div v-for='book in books' v-if='book.price < 1500'>
```


**``v-for``ディレクティブは``v-if``よりも高い優先度**です。言いかえると、内部で**エレメントをループしてから、``v-if``で条件をチェック**しています。例えば下記コードの様な感じです。

```javascript

this.books.map(function (book) {
  if (books.price < 1500) {
    return book
  }
})
```

ということは、``books``から数個のエレメントを描画したい時、``books配列``全体を繰り返し処理する必要があるということです。あまりいい動きではありません。

解決案は`computedプロパティ`を使うことです。下記の様に置き換えられます。

```vuejs
<div v-for='book in reasonableBooks'>
```

```javascript
computed: {
  reasonableBooks: () => {
    return this.books.filter(function (book) {
      return books.price < 1500
    })
  }
}
```


この方法が良い理由として、下記が挙げられます。

- 繰り返し処理を全てのエレメントに対し行わないので、より効率良く描画できる
- 依存関係が変更された時に、フィルターされたリストのみ再評価される
- テンプレートのコンポーネントロジックを分割できる。結果的に読みやすいコンポーネントになる


# 7. Propsのバリデートをしっかりと定義する

アプリが大きくなればなるほど、propsで使われているフォーマットやデータタイプ、その他の規約を忘れてしまいやすいと思います。特に大規模な開発チームに属している場合、自分の作ったコンポーネントをどの様に使えば良いか、チームメンバーはひと目ではわかりません。

チームメンバーがあなたが作ったコンポーネントのpropsのフォーマットなどを確認する時間を少しでも省く為に、propsのバリデーションはしっかりと指定しましょう。

詳しくは**[公式サイト(プロパティのバリデーション)](https://jp.vuejs.org/v2/guide/components-props.html#%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%81%AE%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)** がお勧めです。

```javascript
props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].indexOf(value) !== -1
    }
  }
}
```


# 8. コンポーネント名にはパスカルケースかケバブケースを使う

コンポーネント名にはパスカルケース(PascalCase)かケバブケース(kebab-case)を使うという規約があります。どちらを使っても問題ありませんが、どちらか一方にしぼりましょう。

ほとんどのIDEのオートコンプリート機能がサポートしているため、パスカルケースがお勧めです。

```html
# 悪い例

mycomponent.vue
myComponent.vue
Mycomponent.vue

# 良い例

MyComponent.vue
my-component.vue
```

# 9. 基底コンポーネント名にはプレフィックスをつける
ボタンやアイコンなど、アプリ内の複数のコンポーネントで使用するコンポーネントを**[基底コンポーネント（Base components）](https://jp.vuejs.org/v2/guide/components-registration.html#%E5%9F%BA%E5%BA%95%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E8%87%AA%E5%8B%95%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E7%99%BB%E9%8C%B2)** と呼びます。

**[Vueスタイルガイド](https://jp.vuejs.org/v2/style-guide/#%E5%8D%98%E4%B8%80%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E5%90%8D-%E5%BC%B7%E3%81%8F%E6%8E%A8%E5%A5%A8)** によると、下記の**3つのコンポーネントのみ**含んでいるものが基底コンポーネントです。

- HTMLエレメント
- 他の基底コンポーネント
- 第３者のUIコンポーネント

これらのコンポーネントの命名規則は、`Base`、`V`、または`App`等、特定のプレフィックスをつけることです。どれを使っても大丈夫ですが、どれか１つに絞って使う様にしましょう。

```
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue

components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
```

この命名規約に従うと、ファイルシステムで基底コンポーネントをアルファベット順にグループ化してくれます。また、webpackのインポート関数を使うことによって、この命名規則のパターンにマッチするコンポーネントを探し、Vueプロジェクトのグローバルに自動的にインポートしてくれます。

- [基底コンポーネントの自動グローバル登録](https://jp.vuejs.org/v2/guide/components-registration.html#%E5%9F%BA%E5%BA%95%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E8%87%AA%E5%8B%95%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E7%99%BB%E9%8C%B2)
- [基底コンポーネントの名前](https://jp.vuejs.org/v2/style-guide/#%E5%9F%BA%E5%BA%95%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E5%90%8D%E5%89%8D-%E5%BC%B7%E3%81%8F%E6%8E%A8%E5%A5%A8)
- [プレフィックスとは](https://wa3.i-3-i.info/word1208.html)

# 10. `destroy()`の`$off`でイベントリスナを削除する

``$on``を使ってイベントを監視した場合、`destroyed()`で`$off`を使いリスナを削除するのを忘れない様にしましょう。メモリーリークを防ぐことができます。

- [vm-on](https://jp.vuejs.org/v2/api/#vm-on)
- [vm-off](https://jp.vuejs.org/v2/api/#vm-off)

# 11. 一度だけ宣言・使用されるコンポーネント名にはプレフィックス「The」をつける
基底コンポーネントと似たものに、単一インスタンスコンポーネントがあります。すべてのページで**一度だけ使用され、propsを使用していないコンポーネント**です。例えば、ヘッダーやサイドバー、フッターなどです。

一つのアクティブなインスタンスしか存在しえないことを示す為に、単一インスタンスコンポーネント名にはプレフィックス「The」をつけましょう。

**[単一インスタンスのコンポーネント名についてはこちら](https://jp.vuejs.org/v2/style-guide/#%E5%8D%98%E4%B8%80%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E5%90%8D-%E5%BC%B7%E3%81%8F%E6%8E%A8%E5%A5%A8)**

```
components/
|- TheHeading.vue
|- TheSidebar.vue
|- TheFooter.vue
```


# 12. ミックスインプロパティには`$_`を使う

**[ミックスイン (mixin)](https://jp.vuejs.org/v2/guide/mixins.html)**は、繰り返し利用されるコードを１つのブロックに入れて何度も再利用可能にするための方法です。しかし、幾つか問題があります。その一つが**プロパティのオーバーラップ**です。

コンポーネントにミックスインをインポートすると、コンポーネントコードにミックスインコードが結合されます。もし、同じ名前のプロパティがあったらどうなるでしょう？

この場合、コンポーネントプロパティが優先されます。コンポーネントプロパティは、ミックスインプロパティよりも優先度が高いためです。ミックスインに高い優先度を持たせたい時、命名規則に基づいた名前をミックスインにつけることでオーバーラッピングを防ぐことができます。

コンポーネントプロパティとミックスインプロパティを区別するには、`$_`を使います。`$_`を使う理由として、

- VueJSの命名規則として定義されている
- `_`はVueのプライベートプロパティを定義するために既に使用されているため、使用できない
- `$`はVueのエコシステムで特別なインスタンスのプロパティのため、使用できない

`$_createUser`のように、`$_`をつけることで読みやすくなり、コンポーネントとミックスインの区別がしやすくなります。

# 13. ディレクティブの書き方には一貫性を持たせる

`v-on`などのディレクティブには[省略記法](https://jp.vuejs.org/v2/guide/syntax.html#v-on-%E7%9C%81%E7%95%A5%E8%A8%98%E6%B3%95)があります。

- `v-on` → `@`
- `v-bind` → `:`
- `v-slot` → `#`

省略記法を使うことは全く問題ありませんがどちらか一方を使い、プロジェクト内で一貫性を持たせましょう。結果的に読みやすいコードになります。


# 14. ``created``と``watch``でメソッドを呼ばない

初心者によくある間違いは、`created`と`watch`両方でメソッドを呼んでしまうことです。コンポーネントがイニシャライズされた時に`watch`フックを動かしたいがためです。

```javascript
// 悪い例

created: () {
  this.changeName()
},
methods: {
  changeName() {
    console.log('this is my name!');
  }
},
watch () {
  property() {
    this.changeName()
  }
}
```

一見問題ない様に見えますが、`created()`と`watch()`でコードが重複しています。

解決方法として、関数を`watch()`に移動し、`immediate`と`handler`プロパティを宣言すると、コンポネントがイニシャライズされた時に関数が実行されます。


1. `handler (newVal, oldVal)` → watcherメソッドのことです
2. `immediate: true` → インスタンスが作られた時にハンドラーが動くようになります

```vue.js

// 良い例
watch: {
  myProperty: {
    immediate: true,
    handler() {
      this.changeName();
    }
  }
},
methods: {
  changeName() {
     console.log('this is my name!');
  }
}


// さらに良い例
watch: {
  myProperty: {
    immediate: true,
    handler() {
      console.log('this is my name!'); // 一文なのでmethodsに関数を定義しない
    }
  }
}
```

# 15. テンプレート構文には基本的なJavaScript構文しか含めない
テンプレートの中にロジックを書きたくなる気持ちはわかりますが、テンプレートのコードが複雑になり、読みにくくなってしまいます。

```html
<!-- 悪い例 -->

<p>
 {{
   function () {
    return this.fullName.split(" ").map(function (word) {
      return word[0][0].toUpperCase()
    }).join('')
  }
 }}
</p>
```

基本的に、テンプレート内で何が表示されるかは直感的にわかるようにしたいですよね。そうするために、上記の様な複雑な構文は名前をつけ、適切なコンポーネントオプションに入れましょう。

複雑な構文を分割すると再利用できるのも大きな理由です。

```html
<p>{{ initialName }}<p>
```

```vue.js
computed: {
  initialName: function () {
    return this.fullName.split(" ").map(function (word) {
      return word[0][0].toUpperCase()
    }).join('')
  }
}
```

# JavaScript+Vueスクール参加者募集中です
**[株式会社X-HACK](https://x-hack.connpass.com/)**では、JavaScript+Vueを学びたい方を募集しています。興味があれば、ぜひお越しください。詳細は、**[CONNPASSページ](https://x-hack.connpass.com/event/163703/)**よりどうぞ！

# 参考サイト
- [12 Vue.js Best Practices for Pro Developers](https://medium.com/better-programming/12-vuejs-best-practices-for-pro-developers-28d1f629018c)
- [VueJs best practices ✓](https://blog.usejournal.com/vue-js-best-practices-c5da8d7af48d)