# スタイルガイド

このドキュメントは、 Vue 固有の記法についての公式なスタイルガイドです。もしあなたがプロジェクトにおいて Vue を使用する場合は、エラーや有益でない議論、アンチパターンを避けるための参考となります。しかし、スタイルガイドはすべてのチームやプロジェクトで理想とは限らないと考えていますので、過去の経験や、周囲の技術スタック、個人の価値観に基づいた上で必要に応じて慎重に逸脱することが推奨されます。

ほとんどのパートにおいて、基本的に JavaScript や HTML に対する提案は避けています。セミコロンやカンマの使用の是非はどちらでも良いです。 HTML の属性に対してシングルクォートかダブルクォートどちらかを利用するかもどちらでも良いです。しかし、 Vue のコンテキストにおいて特定のパターンが役立つと判明した場合については、その限りではありません。

最後に、私たちはルール群を 4 つのカテゴリに分割しました:

# ルールカテゴリ

# 優先度 A: 必須

これらのルールはエラー防止に役立ちます。ですので、学び、遵守してください。例外は存在するかもしれませんが、それらは極めて稀で、かつ JavaScript と Vue の両方の専門知識を持った人によってのみ作られるべきです。

# 優先度 B: 強く推奨

これらのルールは、ほとんどのプロジェクトで読みやすさや開発者の体験をよりよくするために見いだされました。これらに違反してもあなたのコードは動きますが、ごくまれなケースで、かつちゃんと正当を示した上でのみ違反するようにすべきです。

# 優先度 C: 推奨

同じくらい良いオプションが複数ある場合、一貫性を確保するために任意の選択をすることができます。これらのルールでは、それぞれ許容可能なオプションを説明し、既定の選択を提案します。つまり、一貫性があり、正当な理由を持ち続ける限り、独自のコードベースで自由に異なる選択肢を作ることができます。ですが、正当な理由を必ず持つようにしてください!コミュニティの標準に合わせることで、あなたは:

  1. 直面するコミュニティのコードを容易に理解できるように脳を慣れさせます。
  2. ほとんどのコミュニティのコードサンプルを変更なしにコピーして貼り付ける事ができます。
  3. 少なくとも Vue に関しては、ほとんどの場合、新たな人材はあなたのコーディングスタイルよりも既に慣れ親しんだものを好みます。

# 優先度 D: 使用注意

Vue のいくつかの機能は、レアケースまたは従来のコードベースからスムーズな移行に対応するために存在します。しかしながらこれを使いすぎると、コードを保守することが難しくなり、またバグの原因になることさえあります。これらのルールは潜在的な危険な機能を照らし、いつ、なぜ避けなかればならないのかを説明しています。

# 優先度 A ルール: 必須 (エラー防止)

# 複数単語のコンポーネント名 必須

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

全ての HTML 要素は 1 単語なので、このルールを守ることで既に存在する HTML 要素や将来定義される HTML 要素との衝突を防止することができます (opens new window)

悪い例

app.component('todo', {
  // ...
})
1
2
3
export default {
  name: 'Todo',
  // ...
}
1
2
3
4

良い例

app.component('todo-item', {
  // ...
})
1
2
3
export default {
  name: 'TodoItem',
  // ...
}
1
2
3
4

# Prop の定義 必須

Prop の定義は可能な限り詳細にするべきです。

コミットされたコードでは、prop の定義は常に可能な限り詳細にすべきで、少なくともタイプの指定をする必要があります。

詳細な説明

詳細なプロパティの定義 には 2 つの利点があります:

  • コンポーネントの API が明文化されるため、そのコンポーネントの使用方法が簡単に確認できます。
  • 開発中、コンポーネントに対して誤った形式のプロパティが提供されると Vue は警告を通知するため、潜在的なエラー原因の検知に役立ちます。

悪い例

// プロトタイピングの時に限り OK
props: ['status']
1
2

良い例

props: {
  status: String
}
1
2
3
// さらに良いです!
props: {
  status: {
    type: String,
    required: true,

    validator: value => {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].includes(value)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# キー付き v-for 必須

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

サブツリーの内部コンポーネントの状態を維持するために、コンポーネントでの v-for には 常に key を付ける必要があります。ただし要素の場合であっても、アニメーションでのオブジェクトの一貫性 (opens new window)のように、予測可能な振る舞いを維持することをお勧めします。

詳細な説明

TODO リストを持っているとしましょう:

data() {
  return {
    todos: [
      {
        id: 1,
        text: 'Learn to use v-for'
      },
      {
        id: 2,
        text: 'Learn to use key'
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

次に、それらをアルファベット順に並べ替えます。 DOM を更新するとき、可能な限り安価な DOM の変更を行うために Vue はレンダリングを最適化します。 これは、最初の todo 要素を削除して、再度リストの最後に追加することを意味するかもしれません。

問題は、DOM に残る要素を削除しないことが重要となる場合があることです。 例えば、 <transition-group> を使用してリストの並べ替えをアニメーション化する場合だったり、レンダリングされた要素が <input> の時はフォーカスを維持したいといった場合があります。 このような場合、アイテムごとに一意のキー (:key="todo.id" など) を追加することで、 Vue に対してどうしたらより予期した通りの動作ができるかを伝えることができます。

これまでの経験から、あなたとあなたのチームがこれらのエッジケースについて心配する必要がないように、 常に 一意のキーを追加することをお勧めします。 その上で、オブジェクトの一貫性が必要なくてパフォーマンスが重要な稀なシナリオにおいては、意識的な例外を作成すると良いでしょう。

悪い例

<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>
1
2
3
4
5

良い例

<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>
1
2
3
4
5
6
7
8

# v-for と一緒に v-if を使うのを避ける 必須

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

こうしたくなる一般的なケースが 2 通りほどあります:

  • リストのアイテムをフィルタリングする時 (v-for="user in users" v-if="user.isActive" のように)。このような場合、 users をフィルタリングをされたリストを返す新しい算出プロパティ (例えば activeUsers) に置き換えます。

  • リストを非表示にする必要がある場合に、リストがレンダリングされるのを避ける時 (v-for="user in users" v-if="shouldShowUsers" のように)。このような場合、 v-if をコンテナ要素 (例えば ul, ol)に移動します。

詳細な説明

Vue がディレクティブを処理する場合、v-forv-if よりも優先度が高いため、次のようなテンプレートは:

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
9

v-if ディレクティブが最初に評価され、反復変数の user がこの時点では存在しないためエラーが投げられます。

これは、代わりに算出プロパティを元に反復処理をすることで修正できます。次のようになります:

computed: {
  activeUsers() {
    return this.users.filter(user => user.isActive)
  }
}
1
2
3
4
5
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8

または、 v-for と一緒に <template> タグを使用して、 <li> 要素をラップすることもできます:

<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>
1
2
3
4
5
6
7

悪い例

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
9

良い例

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>
1
2
3
4
5
6
7

# コンポーネントスタイルのスコープ 必須

アプリケーションにとって、トップレベルの App コンポーネントとレイアウトコンポーネントのスタイルはグローバルである可能性がありますが、他のすべてのコンポーネントは常にスコープ化されているべきです。

これは、単一ファイルコンポーネントのみに関連します。scoped 属性 (opens new window)の使用は必須_ではありません_。 スコープは CSS modules (opens new window)BEM (opens new window) のようなクラスに基づいた戦略、または他の ライブラリ/慣例 を介して行うことができます。

ただしコンポーネントライブラリでは、 scoped 属性を使用するのではなく、クラスに基づいた戦略を優先すべきです

これにより、人間が読み取りやすいクラス名を使って、内部のスタイルを上書きすることが容易になります。またそのクラス名は、高い特定性を持たないけれど、依然として競合が発生する可能性が低いままのものになります。

詳細な説明

大規模なプロジェクトを開発している場合や他の開発者と一緒に開発している場合、またはサードパーティの HTML/CSS (Auth0 など) を含んでいる場合は、一貫したスコープによってスタイルが対象のコンポーネントのみに適用されることが保証されます。

scoped 属性以外にも、一意のクラス名を使用することでサードパーティの CSS が独自の HTML に適用されないことを保証しやすくできます。例えば、多くのプロジェクトでは buttonbtn 、または icon といったクラス名を使用しているため、BEM などの戦略を使用していない場合でも、アプリ固有 かつ/または コンポーネント固有(例: ButtonClose-icon)のプレフィックスを追加することで、ある程度の保護を提供できます。

悪い例

<template>
  <button class="btn btn-close">×</button>
</template>

<style>
.btn-close {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9

良い例

<template>
  <button class="button button-close">×</button>
</template>

<!-- `scoped` を使用 -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
  <button :class="[$style.button, $style.buttonClose]">×</button>
</template>

<!-- Using CSS modules -->
<style module>
.button {
  border: none;
  border-radius: 2px;
}

.buttonClose {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
  <button class="c-Button c-Button--close">×</button>
</template>

<!-- BEM の慣例を使用 -->
<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# プライベートなプロパティ名 必須

モジュールスコープを使用して、外部からプライベート関数にアクセスできないようにします。それが不可能な場合は、パブリック API と見なすべきではないプラグインやミックスインなどのカスタムプライベートプロパティに、常に $_ のプレフィックスを使用してください。 その上で、他の作成者によるコードとの競合を避けるために、名前付きスコープも含めるようにしてください (例: $_yourPluginName_)

詳細な説明

Vue は _ のプレフィックスを使用して独自のプライベートプロパティを定義するため、同じプレフィックス (_update など) を使用すると、インスタンスプロパティが上書きされるリスクがあります。 Vue が現在特定のプロパティ名を使用していないことを確認したとしても、それ以降のバージョンで競合が発生しないという保証はありません。

$ のプレフィックスに関しては、Vue エコシステム内でのそのプレフィックスの目的は、ユーザーに公開される特別なインスタンスプロパティであるため、_独自_プロパティに使用することは適切ではありません。

代わりに、Vue との競合がないことを保証するユーザー定義のプライベートプロパティの規則として、2 つのプレフィックスを $_ に結合することをお勧めしています。

悪い例

const myGreatMixin = {
  // ...
  methods: {
    update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
const myGreatMixin = {
  // ...
  methods: {
    _update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
const myGreatMixin = {
  // ...
  methods: {
    $update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
const myGreatMixin = {
  // ...
  methods: {
    $_update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8

良い例

const myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
// さらに良いです!
const myGreatMixin = {
  // ...
  methods: {
    publicMethod() {
      // ...
      myPrivateFunction()
    }
  }
}

function myPrivateFunction() {
  // ...
}

export default myGreatMixin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 優先度B のルール: 強く推奨 (読みやすさの向上)

# コンポーネントのファイル 強く推奨

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

そうすれば、コンポーネントを編集したり使い方を確認するときにより素早く見つけることができます。

悪い例

app.component('TodoList', {
  // ...
})

app.component('TodoItem', {
  // ...
})
1
2
3
4
5
6
7

良い例

components/
|- TodoList.js
|- TodoItem.js
1
2
3
components/
|- TodoList.vue
|- TodoItem.vue
1
2
3

# 単一ファイルコンポーネントのファイル名の形式 強く推奨

単一ファイルコンポーネントのファイル名は、すべてパスカルケース (PascalCase) にするか、すべてケバブケース (kebab-case) にするべきです。

パスカルケースは、JS(X) やテンプレートの中でコンポーネントを参照する方法と一致しているので、コードエディタ上でオートコンプリートが可能な場合はとてもうまく働きます。しかし、大文字と小文字が混ざったファイル名は、大文字と小文字を区別しないファイルシステム上で時々問題を起こす可能性があります。そのため、ケバブケースもまた完全に受け入れられています。

悪い例

components/
|- mycomponent.vue
1
2
components/
|- myComponent.vue
1
2

良い例

components/
|- MyComponent.vue
1
2
components/
|- my-component.vue
1
2

# 基底コンポーネントの名前 強く推奨

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

詳細な説明

これらのコンポーネントは、あなたのアプリケーションに一貫したスタイルやふるまいをもたせる基礎として位置づけられます。これらは、おそらく以下のものだけを含むでしょう:

  • HTML 要素、
  • 別の基底コンポーネント、そして
  • サードパーティ製の UI コンポーネント

しかし、それらにはグローバルな状態(例:Vuex ストアからのもの)は含まれません

これらのコンポーネントの名前は、しばしばラップしている要素の名前を含みます(例えば BaseButtonBaseTable)。それ特有の目的のための要素がない場合は別ですが(例えば BaseIcon)。もっと特定の用途に向けた同じようなコンポーネントを作る時は、ほとんどすべての場合にこれらのコンポーネントを使うことになるでしょう。(例えば BaseButtonButtonSubmit で使うなど)

このルールの長所:

  • エディタ上でアルファベット順に並べられた時に、アプリケーションの基底コンポーネントはすべて一緒にリストされ、識別しやすくなります。

  • コンポーネントの名前は常に複数単語にするべきなので、このルールによってシンプルなコンポーネントラッパーに勝手なプレフィックスを選ばなければならない(例えば MyButtonVueButton)ということがなくなります。

  • これらのコンポーネントはとても頻繁に使われるので、あらゆる場所で import するよりも単純にグローバルにしてしまいたいと思うかもしれません。プレフィックスを利用して、それを Webpack でできるようになります。

    const requireComponent = require.context("./src", true, /Base[A-Z]\w+\.(vue|js)$/)
    requireComponent.keys().forEach(function (fileName) {
      let baseComponentConfig = requireComponent(fileName)
      baseComponentConfig = baseComponentConfig.default || baseComponentConfig
      const baseComponentName = baseComponentConfig.name || (
        fileName
          .replace(/^.+\//, '')
          .replace(/\.\w+$/, '')
      )
      app.component(baseComponentName, baseComponentConfig)
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

悪い例

components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
1
2
3
4

良い例

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
1
2
3
4
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
1
2
3
4
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
1
2
3
4

# 単一インスタンスのコンポーネント名 強く推奨

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

これはそのコンポーネントが 1 つのページでしか使われないということを意味するのではなく、ページごとに 1 回しか使われないという意味です。これらのコンポーネントは、アプリケーション内のコンテキストではなく、アプリケーションに対して固有のため、決してプロパティを受け入れることはありません。もしプロパティを追加する必要があることに気づいたのなら、それは 現時点で ページごとに 1 回しか使われていないだけで、実際には再利用可能なコンポーネントだということを示すよい目印です。

悪い例

components/
|- Heading.vue
|- MySidebar.vue
1
2
3

良い例

components/
|- TheHeading.vue
|- TheSidebar.vue
1
2
3

# 密結合コンポーネントの名前 強く推奨

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

もし、コンポーネントが単一の親コンポーネントの中でだけ意味をもつものなら、その関連性は名前からはっきりわかるようにするべきです。一般的にエディタはファイルをアルファベット順に並べるので、関連をもつものどうしが常に隣り合って並ぶことにもなります。

詳細な説明

この問題を、子コンポーネントを親コンポーネントの名前を元に命名したディレクトリの中に入れることで解決したいと思うかもしれません。例えば:

components/
|- TodoList/
   |- Item/
      |- index.vue
      |- Button.vue
   |- index.vue
1
2
3
4
5
6

もしくは:

components/
|- TodoList/
   |- Item/
      |- Button.vue
   |- Item.vue
|- TodoList.vue
1
2
3
4
5
6

これは推奨されません。以下のような結果を生むからです:

  • 同じような名前のファイルがたくさんできてしまい、コードエディタ上で素早くファイルを切り替えるのが難しくなります。
  • ネストしたサブディレクトリがたくさんできてしまい、エディタのサイドバーでコンポーネントを参照するのに時間がかかるようになります。

悪い例

components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
1
2
3
4
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
1
2
3

良い例

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
1
2
3
4
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
1
2
3

# コンポーネント名における単語の順番 強く推奨

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

詳細な説明

あなたは疑問に思うかもしれません:

“なぜコンポーネント名に自然な言語でないものを使うように強制するのですか?”

自然な英語では、形容詞やその他の記述子は一般的に名詞の前に置かれ、そうでない場合には接続詞が必要になります。例えば:

  • Coffee with milk
  • Soup of the day
  • Visitor to the museum

もちろん、あなたがそうしたいのならば、これらの接続詞をコンポーネント名に含めても構いませんが、それでも順番は重要です。

また、 何を「最高レベル」として尊重するかがアプリケーションの文脈になる ことに注意してください。例えば、検索フォームを持ったアプリケーションを想像してください。こんなコンポーネントがあるかもしれません:

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
1
2
3
4
5
6
7

あなたも気づいたと思いますが、これではどのコンポーネントが検索に特有のものなのかとても分かりづらいです。では、このルールに従ってコンポーネントの名前を変えてみましょう。

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue
1
2
3
4
5
6
7

一般的にエディタではファイルはアルファベット順に並ぶので、コンポーネント間のあらゆる重要な関連性はひと目ではっきりと分かります。

あなたは、これを別の方法で解決したいと思うかもしれません。つまり、すべての検索コンポーネントは search ディレクトリの下に、すべての設定コンポーネントは settings ディレクトリの下にネストするという方法です。以下の理由から、とても大規模なアプリケーション(例えば 100 以上のコンポーネントがあるような)の場合に限ってこのアプローチを考慮することを推奨します:

  • 一般的に、入れ子のサブディレクトリの中を移動するのは、単一の components ディレクトリをスクロールするのと比べて余分に時間がかかります。
  • 名前の競合(複数の ButtonDelete.vue コンポーネントなど)により、コードエディタで特定のコンポーネントに素早く移動しづらくなります。
  • 移動したコンポーネントへの相対参照を更新するには、検索と置換だけでは不十分な場合が多いため、リファクタリングはより困難になります。

悪い例

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
1
2
3
4
5
6
7

良い例

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
1
2
3
4
5
6
7

# 自己終了形式のコンポーネント 強く推奨

単一ファイルコンポーネント、文字列テンプレート、および JSX の中にある、中身を持たないコンポーネントは自己終了形式で書くべきです。ただし、DOM テンプレート内ではそうしてはいけません。

自己終了形式のコンポーネントは、単に中身を持たないだけでなく、中身を持たないことを 意図した ことだとはっきりと表現します。本の中にある白紙のページと、「このページは意図的に白紙のままにしています」と書かれたページとは違うということです。また、不要な閉じタグがなくなることであなたのコードはより読みやすくなります。

残念ながら、HTML はカスタム要素の自己終了形式を許していません。公式の「空」要素 (opens new window) だけです。これが、Vue のテンプレートコンパイラが DOM よりも先にテンプレートにアクセスして、その後 DOM の仕様に準拠した HTML を出力することができる場合にだけこの方策を使うことができる理由です。

悪い例

<!-- 単一ファイルコンポーネント、文字列テンプレート、JSX の中 -->
<MyComponent></MyComponent>
1
2
<!-- DOM テンプレートの中 -->
<my-component/>
1
2

良い例

<!-- 単一ファイルコンポーネント、文字列テンプレート、JSX の中 -->
<MyComponent/>
1
2
<!-- DOM テンプレートの中 -->
<my-component></my-component>
1
2

# テンプレート内でのコンポーネント名の形式 強く推奨

ほとんどのプロジェクトにおいて、単一ファイルコンポーネント と文字列テンプレートの中では、コンポーネント名は常にパスカルケース(PascalCase)になるべきです。 - しかし、 DOM テンプレートの中ではケバブケース(kebab-case)です。

パスカルケースには、ケバブケースよりも優れた点がいくつかあります:

  • パスカルケースは JavaScript でも使われるので、エディタがテンプレート内のコンポーネント名を自動補完できます。
  • <MyComponent><my-component> よりも一単語の HTML 要素との見分けがつきやすいです。なぜなら、ハイフン 1 文字だけの違いではなく 2 文字(2 つの大文字) の違いがあるからです。
  • もし、テンプレート内で、Vue 以外のカスタム要素(例: Web コンポーネントなど)を使っていたとしても、パスカルケースは Vue コンポーネントがはっきりと目立つことを保証します。

残念ですが、HTML は大文字と小文字を区別しないので、DOM テンプレートの中ではまだケバブケースを使う必要があります。

ただし、もしあなたが既にケバブケースを大量に使っているのなら、HTML の慣習との一貫性を保ちすべてのあなたのプロジェクトで同じ型式を使えるようにすることはおそらく上にあげた利点よりも重要です。このような状況では、 どこでもケバブケースを使うのもアリです。

悪い例

<!-- 単一ファイルコンポーネント、文字列テンプレートの中 -->
<mycomponent/>
1
2
<!-- 単一ファイルコンポーネント、文字列テンプレートの中 -->
<myComponent/>
1
2
<!-- DOM テンプレートの中 -->
<MyComponent></MyComponent>
1
2

良い例

<!-- 単一ファイルコンポーネント、文字列テンプレートの中 -->
<MyComponent/>
1
2
<!-- DOM テンプレートの中 -->
<my-component></my-component>
1
2

または

<!-- どこでも -->
<my-component></my-component>
1
2

# JS/JSX 内でのコンポーネント名の形式 強く推奨

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

詳細な説明

JavaScript では、クラスやプロトタイプのコンストラクタは - 原則として異なるインスタンスを持ちうるものはすべて- パスカルケースにするのがしきたりです。Vue コンポーネントもインスタンスをもつので、同じようにパスカルケースにするのが理にかなっています。さらなる利点として、JSX(とテンプレート)の中でパスカルケースを使うことによって、コードを読む人がコンポーネントと HTML 要素をより簡単に見分けられるようになります。

しかし、app.component によるグローバルコンポーネント定義だけを使うアプリケーションでは、代わりにケバブケースを使うことを推奨します。理由は以下の通りです:

  • グローバルコンポーネントを JavaScript から参照することはほとんどないので、 JavaScript の原則に従う意味もほとんどありません。
  • そのようなアプリケーションはたくさんの DOM 内テンプレート をもつのが常ですが、 そこでは ケバブケースを 必ず 使う必要があります

悪い例

app.component('myComponent', {
  // ...
})
1
2
3
import myComponent from './MyComponent.vue'
1
export default {
  name: 'myComponent',
  // ...
}
1
2
3
4
export default {
  name: 'my-component',
  // ...
}
1
2
3
4

良い例

app.component('MyComponent', {
  // ...
})
1
2
3
app.component('my-component', {
  // ...
})
1
2
3
import MyComponent from './MyComponent.vue'
1
export default {
  name: 'MyComponent',
  // ...
}
1
2
3
4

# 完全な単語によるコンポーネント名 強く推奨

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

長い名前によってもたらされる明快さは非常に貴重ですが、それをタイプする労力はエディタの自動補完によってとても小さくなります。特に、一般的でない略語は常に避けるべきです。

悪い例

components/
|- SdSettings.vue
|- UProfOpts.vue
1
2
3

良い例

components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
1
2
3

# プロパティ名の型式 強く推奨

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

私たちは単純にこの慣習に従っています。JavaScript の中ではキャメルケースがより自然で、HTML の中ではケバブケースが自然です。

悪い例

props: {
  'greeting-text': String
}
1
2
3
<WelcomeMessage greetingText="hi"/>
1

良い例

props: {
  greetingText: String
}
1
2
3
<WelcomeMessage greeting-text="hi"/>
1

# 複数の属性をもつ要素 強く推奨

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

JavaScript では、複数のプロパティをもつ要素を複数の行に分けて書くことはよい慣習だと広く考えられています。なぜなら、その方がより読みやすいからです。Vue のテンプレートや JSX も同じように考えることがふさわしいです。

悪い例

<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
1
<MyComponent foo="a" bar="b" baz="c"/>
1

良い例

<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>
1
2
3
4
<MyComponent
  foo="a"
  bar="b"
  baz="c"
/>
1
2
3
4
5

# テンプレート内での単純な式 強く推奨

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

テンプレート内に複雑な式があると、テンプレートが宣言的ではなくなります。私たちは、どのように その値を算出するかではなく、何が 表示されるべきかを記述するように努力するべきです。また、算出プロパティやメソッドによってコードが再利用できるようになります。

悪い例

{{
  fullName.split(' ').map((word) => {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}
1
2
3
4
5

良い例

<!-- テンプレート内 -->
{{ normalizedFullName }}
1
2
// 複雑な式を算出プロパティに移動
computed: {
  normalizedFullName() {
    return this.fullName.split(' ')
      .map(word => word[0].toUpperCase() + word.slice(1))
      .join(' ')
  }
}
1
2
3
4
5
6
7
8

# 単純な算出プロパティ 強く推奨

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

詳細な説明

単純な、よい名前を持つ算出プロパティは:

  • テストしやすい

    それぞれの算出プロパティが、依存がとても少ないごく単純な式だけを含む場合、それが正しく動くことを確認するテストを書くのがより簡単になります。

  • 読みやすい

    算出プロパティを単純にするということは、たとえそれが再利用可能ではなかったとしても、それぞれに分かりやすい名前をつけることになります。それによって、他の開発者(そして未来のあなた)が、注意を払うべきコードに集中し、何が起きているかを把握することがより簡単になります。

  • 要求の変更を受け入れやすい

    名前をつけることができる値は何でも、ビューでも役に立つ可能性があります。例えば、いくら割引になっているかをユーザに知らせるメッセージを表示することに決めたとします。 また、消費税も計算して、最終的な価格の一部としてではなく、別々に表示することにします。

    小さく焦点が当てられた算出プロパティは、どのように情報が使われるかの決めつけをより少なくし、少しのリファクタリングで要求の変更を受け入れられます。

悪い例

computed: {
  price() {
    const basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0)
    )
  }
}
1
2
3
4
5
6
7
8
9

良い例

computed: {
  basePrice() {
    return this.manufactureCost / (1 - this.profitMargin)
  },

  discount() {
    return this.basePrice * (this.discountPercent || 0)
  },

  finalPrice() {
    return this.basePrice - this.discount
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 引用符付きの属性値 強く推奨

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

HTML では、空白を含まない属性値は引用符でくくらなくてもよいことになっていますが、そのせいで空白の使用を 避けてしまい 属性値が読みづらくなりがちです。

悪い例

<input type=text>
1
<AppSidebar :style={width:sidebarWidth+'px'}>
1

良い例

<input type="text">
1
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
1

# ディレクティブの短縮記法 強く推奨

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

悪い例

<input
  v-bind:value="newTodoText"
  :placeholder="newTodoInstructions"
>
1
2
3
4
<input
  v-on:input="onInput"
  @focus="onFocus"
>
1
2
3
4
<template v-slot:header>
  <h1>Here might be a page title</h1>
</template>

<template #footer>
  <p>Here's some contact info</p>
</template>
1
2
3
4
5
6
7

良い例

<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>
1
2
3
4
<input
  v-bind:value="newTodoText"
  v-bind:placeholder="newTodoInstructions"
>
1
2
3
4
<input
  @input="onInput"
  @focus="onFocus"
>
1
2
3
4
<input
  v-on:input="onInput"
  v-on:focus="onFocus"
>
1
2
3
4
<template v-slot:header>
  <h1>Here might be a page title</h1>
</template>

<template v-slot:footer>
  <p>Here's some contact info</p>
</template>
1
2
3
4
5
6
7
<template #header>
  <h1>Here might be a page title</h1>
</template>

<template #footer>
  <p>Here's some contact info</p>
</template>
1
2
3
4
5
6
7

Component/instance options should be ordered consistently.

This is the default order we recommend for component options. They're split into categories, so you'll know where to add new properties from plugins.

  1. Global Awareness (requires knowledge beyond the component)

    • name
  2. Template Dependencies (assets used in the template)

    • components
    • directives
  3. Composition (merges properties into the options)

    • extends
    • mixins
    • provide/inject
  4. Interface (the interface to the component)

    • inheritAttrs
    • props
    • emits
  5. Composition API (the entry point for using the Composition API)

    • setup
  6. Local State (local reactive properties)

    • data
    • computed
  7. Events (callbacks triggered by reactive events)

    • watch
    • Lifecycle Events (in the order they are called)
      • beforeCreate
      • created
      • beforeMount
      • mounted
      • beforeUpdate
      • updated
      • activated
      • deactivated
      • beforeUnmount
      • unmounted
      • errorCaptured
      • renderTracked
      • renderTriggered
  8. Non-Reactive Properties (instance properties independent of the reactivity system)

    • methods
  9. Rendering (the declarative description of the component output)

    • template/render

The attributes of elements (including components) should be ordered consistently.

This is the default order we recommend for component options. They're split into categories, so you'll know where to add custom attributes and directives.

  1. Definition (provides the component options)

    • is
  2. List Rendering (creates multiple variations of the same element)

    • v-for
  3. Conditionals (whether the element is rendered/shown)

    • v-if
    • v-else-if
    • v-else
    • v-show
    • v-cloak
  4. Render Modifiers (changes the way the element renders)

    • v-pre
    • v-once
  5. Global Awareness (requires knowledge beyond the component)

    • id
  6. Unique Attributes (attributes that require unique values)

    • ref
    • key
  7. Two-Way Binding (combining binding and events)

    • v-model
  8. Other Attributes (all unspecified bound & unbound attributes)

  9. Events (component event listeners)

    • v-on
  10. Content (overrides the content of the element)

    • v-html
    • v-text

You may want to add one empty line between multi-line properties, particularly if the options can no longer fit on your screen without scrolling.

When components begin to feel cramped or difficult to read, adding spaces between multi-line properties can make them easier to skim again. In some editors, such as Vim, formatting options like this can also make them easier to navigate with the keyboard.

Good

props: {
  value: {
    type: String,
    required: true
  },

  focused: {
    type: Boolean,
    default: false
  },

  label: String,
  icon: String
},

computed: {
  formattedValue() {
    // ...
  },

  inputClasses() {
    // ...
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// No spaces are also fine, as long as the component
// is still easy to read and navigate.
props: {
  value: {
    type: String,
    required: true
  },

  focused: {
    type: Boolean,
    default: false
  },

  label: String,
  icon: String
},

computed: {
  formattedValue() {
    // ...
  },

  inputClasses() {
    // ...
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Single-file components should always order <script>, <template>, and <style> tags consistently, with <style> last, because at least one of the other two is always necessary.

Bad

<style>/* ... */</style>
<script>/* ... */</script>
<template>...</template>
1
2
3
<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
1
2
3
4
5
6
7
8
9

Good

<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>
1
2
3
4
5
6
7
8
9
<!-- ComponentA.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
1
2
3
4
5
6
7
8
9

# Element selectors with scoped use with caution

Element selectors should be avoided with scoped.

Prefer class selectors over element selectors in scoped styles, because large numbers of element selectors are slow.

Detailed Explanation

To scope styles, Vue adds a unique attribute to component elements, such as data-v-f3f3eg9. Then selectors are modified so that only matching elements with this attribute are selected (e.g. button[data-v-f3f3eg9]).

The problem is that large numbers of element-attribute selectors (opens new window) (e.g. button[data-v-f3f3eg9]) will be considerably slower than class-attribute selectors (opens new window) (e.g. .btn-close[data-v-f3f3eg9]), so class selectors should be preferred whenever possible.

Bad

<template>
  <button>×</button>
</template>

<style scoped>
button {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9

Good

<template>
  <button class="btn btn-close">×</button>
</template>

<style scoped>
.btn-close {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9

# Implicit parent-child communication use with caution

Props and events should be preferred for parent-child component communication, instead of this.$parent or mutating props.

An ideal Vue application is props down, events up. Sticking to this convention makes your components much easier to understand. However, there are edge cases where prop mutation or this.$parent can simplify two components that are already deeply coupled.

The problem is, there are also many simple cases where these patterns may offer convenience. Beware: do not be seduced into trading simplicity (being able to understand the flow of your state) for short-term convenience (writing less code).

Bad

app.component('TodoItem', {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  template: '<input v-model="todo.text">'
})
1
2
3
4
5
6
7
8
9
10
app.component('TodoItem', {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  methods: {
    removeTodo() {
      this.$parent.todos = this.$parent.todos.filter(todo => todo.id !== vm.todo.id)
    }
  },

  template: `
    <span>
      {{ todo.text }}
      <button @click="removeTodo">
        ×
      </button>
    </span>
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Good

app.component('TodoItem', {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  template: `
    <input
      :value="todo.text"
      @input="$emit('input', $event.target.value)"
    >
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.component('TodoItem', {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  template: `
    <span>
      {{ todo.text }}
      <button @click="$emit('delete')">
        ×
      </button>
    </span>
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# Non-flux state management use with caution

Vuex (opens new window) should be preferred for global state management, instead of this.$root or a global event bus.

Managing state on this.$root and/or using a global event bus (opens new window) can be convenient for very simple cases, but it is not appropriate for most applications.

Vuex is the official flux-like implementation (opens new window) for Vue, and offers not only a central place to manage state, but also tools for organizing, tracking, and debugging state changes. It integrates well in the Vue ecosystem (including full Vue DevTools (opens new window) support).

Bad

// main.js
import { createApp } from 'vue'
import mitt from 'mitt'
const app = createApp({
  data() {
    return {
      todos: [],
      emitter: mitt()
    }
  },

  created() {
    this.emitter.on('remove-todo', this.removeTodo)
  },

  methods: {
    removeTodo(todo) {
      const todoIdToRemove = todo.id
      this.todos = this.todos.filter(todo => todo.id !== todoIdToRemove)
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Good

// store/modules/todos.js
export default {
  state: {
    list: []
  },

  mutations: {
    REMOVE_TODO (state, todoId) {
      state.list = state.list.filter(todo => todo.id !== todoId)
    }
  },

  actions: {
    removeTodo ({ commit, state }, todo) {
      commit('REMOVE_TODO', todo.id)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- TodoItem.vue -->
<template>
  <span>
    {{ todo.text }}
    <button @click="removeTodo(todo)">
      X
    </button>
  </span>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  methods: mapActions(['removeTodo'])
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Deployed on Netlify.
最終更新日: 12/24/2020, 3:05:55 PM