Vue.jsスタイルガイドとeslint-plugin-vue検証ルールのマッピング

この記事は [Vue.js Advent Calendar 2018](https://qiita.com/advent-calendar/2018/vuejs) 5日目の記事です。
気まぐれに覗いたら空いてたのでいただきました。

去年の[Advent Calendar](https://qiita.com/advent-calendar/2017/vue)で@mysticateaさんの記事「[eslint-plugin-vue を作っている話](https://qiita.com/mysticatea/items/860ce3bf0ff387f166ac)」を見て、すごいな[eslint-plugin-vue]！と思って使い始め、ついでにいくつかPRしてたら[eslint-plugin-vue]のコラボレーターになりました:raised_hands:
(追記：現在2019年はメンバーになりました:raised_hands:)
なので[eslint-plugin-vue]ネタで記事書こうと思います。

# この記事について
## 記事の内容

[eslint-plugin-vue]にはVue.jsの公式の[スタイルガイド]を守るためのLintルール（静的検証ルール）がいくつか存在しています。
つまり[eslint-plugin-vue]を使えば自動的に[スタイルガイド]のいくつかの項目を守れるわけです。

しかし、どれがそのLintルールなの？というのを[スタイルガイド]から探そうとすると結構辛いので、
今回はそれをまとめてみようと思います。

（Qiitaだしスタイルガイドは[日本語:flag_jp:のスタイルガイド](https://jp.vuejs.org/v2/style-guide/index.html)を使います。
　この記事には出てこないですが、英語のスタイルガイドは[こちら](https://vuejs.org/v2/style-guide/)です。）



## こんな使い方できるかも

[eslint-plugin-vue]が[スタイルガイド]全ての項目をサポートできているわけではありません。
しかし、一見しても何がサポートされていないのかわからないので、
無駄に人力で、"[`data`が関数で定義されているか](https://eslint.vuejs.org/rules/no-shared-component-data.html)見直す" なんてことをしてしまうかもしれません。

この記事で、[スタイルガイド]のどの項目が自動（[eslint-plugin-vue]）でチェックされないかわかれば、レビューで必要なチェックは何なのか？を考える材料になるかもしれません。というかなったらいいな。

## 調査時点のバージョン
- [eslint-plugin-vue]@7.0.0-alpha.4 [^check-eslint]
- [スタイルガイド] 最終更新日: 2020年5月16日 [^check-style-guide]

[^now]: 現在 2020/05/24 （[eslint-plugin-vue]@v7.0.0-alpha.4）
[^check-eslint]: 2020/05/24に[eslint-plugin-vue]の変更を反映させました。確認したバージョン[eslint-plugin-vue]@v7.0.0-alpha.4
[^check-style-guide]: 2020/05/17に[スタイルガイド]の変更を反映させました。確認したバージョン： [スタイルガイド] 最終更新日: 2020年5月16日

# [スタイルガイド]の各項目と各[Lintルール](https://eslint.vuejs.org/rules/)

[スタイルガイド]の各項目をベースに、その項目についてをチェックできる[eslint-plugin-vue]のルールを書いていきます。
（ここにあるLintルールが[eslint-plugin-vue]のルールの全量ではありません。念のため。）

[eslint-plugin-vue]に対応するルールが存在しないものは、できる限りissueやプルリクエストのリンクを書いておきます。
（実装できそうなのがあればプルリクエストくれたら嬉しいです。）

[スタイルガイド]の各項目も抜粋なので詳細はリンク先を見てください。

## [優先度 A ルール: 必須 (エラー防止)](https://jp.vuejs.org/v2/style-guide/#優先度-A-ルール-必須-エラー防止)

次のeslintの設定で[eslint-plugin-vue]に含まれる「優先度 A」関連のルールを全て有効にできます。

```json
{
  "extends": "plugin:vue/essential"
}
```

### [複数単語コンポーネント名](https://jp.vuejs.org/v2/style-guide/#複数単語コンポーネント名-必須)

**ルートの `App` コンポーネントや、Vue が提供する `<transition>` や `<component>` のようなビルトインコンポーネントを除き、コンポーネント名は常に複数単語とするべきです。**

（早速であれですが、）現在[^now]Lintルールは存在しません。
issueが開かれています。
:exclamation:[Rule proposal: \`multi-word-component-names`](https://github.com/vuejs/eslint-plugin-vue/issues/245)

### [コンポーネントのデータ](https://jp.vuejs.org/v2/style-guide/#コンポーネントのデータ-必須)

**コンポーネントの `data` は関数でなければなりません。** 

対応するLintルール：:white_check_mark: [vue/no-shared-component-data](https://eslint.vuejs.org/rules/no-shared-component-data.html)

### [プロパティの定義](https://jp.vuejs.org/v2/style-guide/#プロパティの定義-必須)

**プロパティの定義はできる限り詳細とするべきです。** 

間接的にLintルールでチェックできます。

対応するLintルール：

- プロパティの型の記述を強制：:white_check_mark: [vue/require-prop-types](https://eslint.vuejs.org/rules/require-prop-types.html)
- `default`の記述を強制：:white_check_mark: [vue/require-default-prop](https://eslint.vuejs.org/rules/require-default-prop.html)

（そもそも無効な`props`の記述もチェックできますがスタイルガイドとは関係無いので今回は書きません）

### [キー付き `v-for`](https://jp.vuejs.org/v2/style-guide/#キー付き-v-for-必須)

**常に `v-for` に対しては `key` を使用してください。** 

対応するLintルール：:white_check_mark: [vue/require-v-for-key](https://eslint.vuejs.org/rules/require-v-for-key.html)

### [`v-for` と一緒に `v-if` を使うのを避ける](https://jp.vuejs.org/v2/style-guide/#v-for-と一緒に-v-if-を使うのを避ける-必須)

**`v-for` と同じ要素に `v-if` を使わないでください。** 

対応するLintルール：:white_check_mark: [vue/no-use-v-if-with-v-for](https://eslint.vuejs.org/rules/no-use-v-if-with-v-for.html)

### [コンポーネントスタイルのスコープ](https://jp.vuejs.org/v2/style-guide/#コンポーネントスタイルのスコープ-必須)

**アプリケーションにとって、トップレベルの `App` コンポーネントとレイアウトコンポーネント内のスタイルはグローバルかもしれませんが、他のすべてのコンポーネントは常にスコープされているべきです。** 

現在[^now]Lintルールは存在しません。
scoped属性についてはissueがいましたが現時点[^now]ではいろいろ問題があるようでcloseされています。
:grey_exclamation:[Rule proposal: \`require-scoped-style-tag`](https://github.com/vuejs/eslint-plugin-vue/issues/448)

[eslint-plugin-vue-scoped-css](https://future-architect.github.io/eslint-plugin-vue-scoped-css/)というプラグインを導入すると、:white_check_mark:[vue-scoped-css/require-scoped](https://future-architect.github.io/eslint-plugin-vue-scoped-css/rules/require-scoped.html)というルールでチェックできます。

### [プライベートなプロパティ名](https://jp.vuejs.org/v2/style-guide/#プライベートなプロパティ名-必須)

**プライベートな関数に外部からアクセスできないようにするために、モジュールスコープを使用してください。それが難しい場合は、プラグインやミックスインなどのプライベートなカスタムプロパティには常に `$_` プレフィックスを使用してください。さらに、他の著者によるコードとの衝突を避けるため、名前付きのスコープを含めてください (例 `$_yourPluginName_` )。**

現在[^now]Lintルールは存在しません。
issueが開かれています。
:exclamation:[Rule proposal: \`private-property-names`](https://github.com/vuejs/eslint-plugin-vue/issues/246)

## [優先度B のルール: 強く推奨 (読みやすさの向上)](https://jp.vuejs.org/v2/style-guide/#優先度B-のルール-強く推奨-読みやすさの向上)

次のeslintの設定で[eslint-plugin-vue]に含まれる「優先度 A・B」関連のルールを全て有効にできます。

```json
{
  "extends": "plugin:vue/strongly-recommended"
}
```

### [コンポーネントのファイル](https://jp.vuejs.org/v2/style-guide/#コンポーネントのファイル-強く推奨)

**ファイルを結合してくれるビルドシステムがあるときは必ず、各コンポーネントはそれぞれ別のファイルに書くべきです。** 

対応するLintルール：:white_check_mark: [vue/one-component-per-file](https://eslint.vuejs.org/rules/one-component-per-file.html)

### [単一ファイルコンポーネントのファイル名の形式](https://jp.vuejs.org/v2/style-guide/#単一ファイルコンポーネントのファイル名の形式-強く推奨)

**[単一ファイルコンポーネント](https://jp.vuejs.org/v2/guide/single-file-components.html) のファイル名は、すべてパスカルケース (PascalCase) にするか、すべてケバブケース (kebab-case) にするべきです。** 

現在[^now]Lintルールは存在しません。
issueがいましたがcloseされています。
:grey_exclamation:[Rule proposal: \`component-filename-casing`](https://github.com/vuejs/eslint-plugin-vue/issues/248)

ファイル名とコンポーネント名を検証するルール[`vue/match-component-file-name`](https://eslint.vuejs.org/rules/match-component-file-name.html)のOptionと、[nameプロパティの形式ルール(`vue/name-property-casing`)](https://eslint.vuejs.org/rules/name-property-casing.html)を複合的に使うことで、このスタイルルールを守る事ができるかもしれません。

### [基底コンポーネントの名前](https://jp.vuejs.org/v2/style-guide/#基底コンポーネントの名前-強く推奨)

**アプリケーション特有のスタイルやルールを適用する基底コンポーネント (またはプレゼンテーションコンポーネント: Presentation Components、ダムコンポーネント: Dumb Components、純粋コンポーネント: Pure Components とも) は、すべて `Base` 、 `App` 、`V` などの固有のプレフィックスで始まるべきです。** 

現在[^now]Lintルールは存在しません。
(自動的に判断するのは難しい気がしますね。いいアイディアがあればissueをお願いします)

### [単一インスタンスのコンポーネント名](https://jp.vuejs.org/v2/style-guide/#単一インスタンスのコンポーネント名-強く推奨)

**常に 1 つのアクティブなインスタンスしか持たないコンポーネントは、1 つしか存在しえないことを示すために `The` というプレフィックスで始めるべきです。** 

現在[^now]Lintルールは存在しません。
(いいアイディアがあればissueをお願いします)

### [密結合コンポーネントの名前](https://jp.vuejs.org/v2/style-guide/#密結合コンポーネントの名前-強く推奨)

**親コンポーネントと密結合した子コンポーネントには、親コンポーネントの名前をプレフィックスとして含むべきです。** 

現在[^now]Lintルールは存在しません。
(いいアイディアがあればissueをお願いします)

### [コンポーネント名における単語の順番](https://jp.vuejs.org/v2/style-guide/#コンポーネント名における単語の順番-強く推奨)

**コンポーネント名は、最高レベルの(たいていは最も一般的な)単語から始めて、説明的な修飾語で終わるべきです。** 

現在[^now]Lintルールは存在しません。
(いいアイディアがあればissueをお願いします)

### [自己終了形式のコンポーネント](https://jp.vuejs.org/v2/style-guide/#自己終了形式のコンポーネント-強く推奨)

**中身を持たないコンポーネントは、 [単一ファイルコンポーネント](https://jp.vuejs.org/v2/guide/single-file-components.html) 、文字列テンプレート、および [JSX](https://jp.vuejs.org/v2/style-guide/render-function.html#JSX) の中では自己終了形式で書くべきです。ただし、DOM テンプレート内ではそうしてはいけません。** 

対応するLintルール：:white_check_mark: [vue/html-self-closing](https://eslint.vuejs.org/rules/html-self-closing.html)

### [テンプレート内でのコンポーネント名の形式](https://jp.vuejs.org/v2/style-guide/#テンプレート内でのコンポーネント名の形式-強く推奨)

**ほとんどのプロジェクトでは、コンポーネント名は [単一ファイルコンポーネント](https://jp.vuejs.org/v2/guide/single-file-components.html) と文字列テンプレートの中では常にパスカルケース(PascalCase)になるべきです。 - しかし、 DOM テンプレートの中ではケバブケース(kebab-case)です。** 

対応するLintルール：:white_check_mark: [vue/component-name-in-template-casing](https://eslint.vuejs.org/rules/component-name-in-template-casing.html)
(少し問題があるため`"plugin:vue/strongly-recommended"`に含まれていません。)

### [JS/JSX 内でのコンポーネント名の形式](https://jp.vuejs.org/v2/style-guide/#JS-JSX-内でのコンポーネント名の形式-強く推奨)

**JS/JSX 内でのコンポーネント名はつねにパスカルケース(PascalCase)にするべきです。ただし、 `Vue.component` で登録したグローバルコンポーネントしか使わないような単純なアプリケーションでは、ケバブケース(kebab-case)を含む文字列になるかもしれません。** 

対応するLintルール：:white_check_mark: [vue/component-definition-name-casing](https://eslint.vuejs.org/rules/component-definition-name-casing.html)  

### [完全な単語によるコンポーネント名](https://jp.vuejs.org/v2/style-guide/#完全な単語によるコンポーネント名-強く推奨)

**コンポーネント名には、略語よりも完全な単語を使うべきです。** 

現在[^now]Lintルールは存在しません。
(いいアイディアがあればissueをお願いします)

### [プロパティ名の型式](https://jp.vuejs.org/v2/style-guide/#プロパティ名の型式-強く推奨)

**プロパティ名は、定義の時は常にキャメルケース(camelCase)にするべきですが、テンプレートや JSX ではケバブケース(kebab-case)にするべきです。** 

対応するLintルール：

- 定義側：:white_check_mark: [vue/prop-name-casing](https://eslint.vuejs.org/rules/prop-name-casing.html)
- テンプレート側：:white_check_mark: [vue/attribute-hyphenation](https://eslint.vuejs.org/rules/attribute-hyphenation.html)

### [複数の属性をもつ要素](https://jp.vuejs.org/v2/style-guide/#複数の属性をもつ要素-強く推奨)

**複数の属性をもつ要素は、1 行に 1 要素ずつ、複数の行にわたって書くべきです。** 

対応するLintルール：:white_check_mark: [vue/max-attributes-per-line](https://eslint.vuejs.org/rules/max-attributes-per-line.html)

### [テンプレート内での単純な式](https://jp.vuejs.org/v2/style-guide/#テンプレート内での単純な式-強く推奨)

**複雑な式は算出プロパティかメソッドにリファクタリングして、コンポーネントのテンプレートには単純な式だけを含むようにするべきです。** 

現在[^now]Lintルールは存在しません。
issueが開かれています。
:exclamation:[Rule proposal: \`simple-expressions-in-templates`](https://github.com/vuejs/eslint-plugin-vue/issues/253)

### [単純な算出プロパティ](https://jp.vuejs.org/v2/style-guide/#単純な算出プロパティ-強く推奨)

**複雑な算出プロパティは、できる限りたくさんの単純なプロパティに分割するべきです。** 

現在[^now]Lintルールは存在しません。
issueが開かれています。
:exclamation:[Rule proposal: \`simple-computed-properties`](https://github.com/vuejs/eslint-plugin-vue/issues/254)

### [引用符付きの属性値](https://jp.vuejs.org/v2/style-guide/#引用符付きの属性値-強く推奨)

**空ではない HTML 属性の値は常に引用符(シングルコーテーションかダブルコーテーション、 JS の中で使われていない方)でくくるべきです。** 

対応するLintルール：:white_check_mark: [vue/html-quotes](https://eslint.vuejs.org/rules/html-quotes.html)

### [ディレクティブの短縮記法](https://jp.vuejs.org/v2/style-guide/#ディレクティブの短縮記法-強く推奨)

**ディレクティブの短縮記法 (`v-bind:` に対する `:` 、 `v-on:` に対する `@` 、 `v-slot:` に対する `#`)は、常に使うか、まったく使わないかのどちらかにするべきです。** 

対応するLintルール：

- `v-bind:`：:white_check_mark: [vue/v-bind-style](https://eslint.vuejs.org/rules/v-bind-style.html)
- `v-on:`：:white_check_mark: [vue/v-on-style](https://eslint.vuejs.org/rules/v-on-style.html)
- `v-slot:`：:white_check_mark: [vue/v-slot-style](https://eslint.vuejs.org/rules/v-slot-style.html) 

## [優先度 C のルール: 推奨 (任意の選択肢と認知上のオーバーヘッドの最小化)](https://jp.vuejs.org/v2/style-guide/#優先度-C-のルール-推奨-任意の選択肢と認知上のオーバーヘッドの最小化)

次のeslintの設定で[eslint-plugin-vue]に含まれる「優先度 A・B・C」関連のルールを全て有効にできます。

```json
{
  "extends": "plugin:vue/recommended"
}
```

### [コンポーネント/インスタンス オプション順序](https://jp.vuejs.org/v2/style-guide/#コンポーネント-インスタンス-オプション順序-推奨)

**コンポーネント/インスタンス オプションは､一貫した順序になるべきです｡** 

対応するLintルール：:white_check_mark: [vue/order-in-components](https://eslint.vuejs.org/rules/order-in-components.html)

### [要素の属性の順序](https://jp.vuejs.org/v2/style-guide/#要素の属性の順序-推奨)

**要素の属性 (コンポーネントを含む) は､一貫した順序になるべきです｡** 

対応するLintルール：:white_check_mark: [vue/attributes-order](https://eslint.vuejs.org/rules/attributes-order.html)

### [コンポーネント/インスタンス オプションの空行](https://jp.vuejs.org/v2/style-guide/#コンポーネント-インスタンス-オプションの空行-推奨)

**特にオプションがスクロールなしでは画面に収まらなくなった場合､複数行に渡るプロパティの間に空行を追加してみてください｡**

現在[^now]Lintルールは存在しません。

### [単一ファイルコンポーネントのトップレベルの属性の順序](https://jp.vuejs.org/v2/style-guide/#単一ファイルコンポーネントのトップレベルの属性の順序-推奨)

**[単一ファイルコンポーネント](https://jp.vuejs.org/v2/guide/single-file-components.html)では､ `<script>` ､ `<template>` ､ `<style>` タグを一貫した順序にするべきです､ `<style>` は最後です､それは他の2つのうち少なくとも1つが常に必要だからです。** 

対応するLintルール：:white_check_mark: [vue/component-tags-order](https://eslint.vuejs.org/rules/component-tags-order.html)

## [優先度 D のルール: 使用注意（潜在的に危険なパターン）](https://jp.vuejs.org/v2/style-guide/#優先度-D-のルール-使用注意（潜在的に危険なパターン）)

### [`key` を使わない `v-if`/`v-else-if`/`v-else`](https://jp.vuejs.org/v2/style-guide/#key-を使わない-v-if-v-else-if-v-else-使用注意)

**それらが同じ種類の要素の場合、通常は `v-if` + `v-else` と一緒に `key` を使用するのが最善です(例: どちらも `<div>` 要素).**

現在[^now]Lintルールは存在しません。
issueが開かれています。
:exclamation:[Rule proposal: \`v-if-else-key`](https://github.com/vuejs/eslint-plugin-vue/issues/255)

### [`scoped` 付きの要素セレクタ](https://jp.vuejs.org/v2/style-guide/#scoped-付きの要素セレクタ-使用注意)

**scoped 付きの要素セレクタは避けるべきです。**

Lintルールは存在しません。
(CSSの話なので**ES**Lintプラグインでは実現されない可能性が高いです。 なので、[stylelint](https://stylelint.io/)を使用して、[selector-max-typeルール](https://stylelint.io/user-guide/rules/selector-max-type)のオプション`0`にしてやるといいと思います。）

### [暗黙的な親子間のやりとり](https://jp.vuejs.org/v2/style-guide/#暗黙的な親子間のやりとり-使用注意)

**親子間のやりとりは、`this.$parent` や変化するプロパティよりも、プロパティとイベントが推奨されます。**

現在[^now]Lintルールは存在しません。
issueが開かれています。
:exclamation:[Rule proposal: \`no-implicit-parent-communication`](https://github.com/vuejs/eslint-plugin-vue/issues/257)

一部の問題は[vue/no-mutating-props](https://eslint.vuejs.org/rules/no-mutating-props.html)ルールで検出できます。

### [Flux 以外の状態管理](https://jp.vuejs.org/v2/style-guide/#Flux-以外の状態管理-使用注意)

**グローバル状態管理には、`this.$root` やグローバルイベントバスよりも、[Vuex](https://github.com/vuejs/vuex) が推奨されます**

現在[^now]Lintルールは存在しません。

---

# あとがき

これまとめてて、[vue/html-quotes](https://eslint.vuejs.org/rules/html-quotes.html)のカテゴリが間違ってる（優先度B扱いじゃなかった）ことに気がついてv5.0.0リリース前に修正できたのはよかった。
まあ僕はほとんどのルールを（[vue/script-indent](https://eslint.vuejs.org/rules/script-indent.html)以外）ONで使ってるのでカテゴリなんてどうでもいいんですけど。

Vueのアドベントカレンダー人気な様子ですぐ埋まってましたね。覗いたら１枠解放されてたのでワーイ＼(^o^)／と思って取っちゃいましたが、おかげで[会社のアドベントカレンダー](https://qiita.com/advent-calendar/2018/future)のネタがなくなった。。。

[eslint-plugin-vue]: https://github.com/vuejs/eslint-plugin-vue
[スタイルガイド]: https://jp.vuejs.org/v2/style-guide/