Edited at
Vue.js #2Day 11

Vue.js スタイルガイド早見表 🎄✨

More than 1 year has passed since last update.

Vue.jsの公式スタイルガイドは長い!!!

サクッっと要点だけまとまったガイドが欲しい。そんなアナタのために書きました。

この記事はVue.jsのスタイルガイドを簡潔にまとめたものです。

( ※部分的に補足を加えたりしています。 )

対象となる人物像: 一度目を通したことがある人 or 時間の無い人 or ザックリでいいから知りたい人


ルールカテゴリ

Vueのスタイルガイドは4つのカテゴリに分けられています。

A > B > C > D の順で優先度 (強制力) が強いです。

A. 必須
B. 強く推奨
C. 推奨
D. 注意(危険)

エラー防止
読みやすさ向上
一貫性の確保
潜在的に危険、
予期せぬ副作用を起こす可能性を事前に回避

📎 [A. 必須] 一覧を見る

📎 [B. 強く推奨] 一覧を見る

📎 [C. 推奨] 一覧を見る

📎 [D. 注意] 一覧を見る


A. 必須


複数単語コンポーネント名

コンポーネント名は複数単語にする。

※将来定義されるHTML要素との衝突を防止するため。

😊 Good

<todo-item />

😰 Bad

<todo />



コンポーネントのデータ

dataプロパティを使用する際は、オブジェクトを返す関数にする。

😊 Good

export default {

data () {
return {
foo: 'bar'
}
}
}

😰 Bad

export default {

data: {
foo: 'bar'
}
}



プロパティの定義

propsプロパティの定義は、できる限り詳細にする。

📎 Vue.jsのpropsカスタムバリデーターを使った堅牢なコンポーネント作成

😊 Good

props: {

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

😰 Bad

props: ['status']



キー付き v-for

v-forにはkeyを付与する。( パフォーマンス向上 )

keyはVirtualDOMのdiffから実際のDOMに反映させるときに、最小限の変更するために使われる。

コストをかけずにDOM変化を実行するためにもkeyを付与するようにする。

📎 React.jsの地味だけど重要なkeyについて

😊 Good

<ul>

<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>

😰 Bad

<ul>

<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>



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

コンポーネントのスタイルは、下記ルールのどれかを採用する。


  1. scoped属性を使用する

  2. CSS modulesを使用する

  3. BEMのようなCSS設計を採用する

😊 Good ( 1. scoped属性を使用する )

<template>

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

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

😊 Good ( 2. CSS modulesを使用する )

<template>

<button :class="[$style.button, $style.buttonClose]">X</button>
</template>

<!-- CSS modules を使用 -->
<style module>
.button {
border: none;
border-radius: 2px;
}
.buttonClose {
background-color: red;
}
</style>

😊 Good ( 3. BEMのようなCSS設計を採用する )

<template>

<button class="c-Button c-Button--close">X</button>
</template>

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

😰 Bad

<template>

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

<!-- CSSがグローバル汚染 -->
<style>
.btn-close {
background-color: red;
}
</style>



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

プラグインやミックスインなどのカスタムプロパティには、$_プレフィックスを付与する。

※コンポーネントのプロパティとの衝突を避けるため

😊 Good


mixins/hoge.js

var myGreatMixin = {

// ...
methods: {
$_myGreatMixin_update: function() {
// ...
}
}
}

😰 Bad


mixins/hoge.js

var myGreatMixin = {

// ...
methods: {
update: function() {
// ...
}
}
}


B. 強く推奨


コンポーネントのファイル

各コンポーネントはそれぞれ別のファイルに書くようにする。

※ コンポーネントが分かれていることで対象コンポーネントを探しやすくなる。可読性があがる。

😊 Good

// ファイルがコンポーネントごとに分けられている

components/
|- TodoList.vue
|- TodoItem.vue

😰 Bad

// ひとつのファイルにコンポーネントを書きまくる

Vue.component('TodoList', {
// ...
})
Vue.component('TodoItem', {
// ...
})



基底コンポーネントの名前

基底コンポーネントは、すべてBaseAppV などの固有のプレフィックスで始める。

😊 Good

components/

|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

😰 Bad

components/

|- MyButton.vue
|- VueTable.vue
|- Icon.vue



単一インスタンスのコンポーネント名

ページごとに1回しか使われないコンポーネントは、Theというプレフィックスで始める。

※1つしか存在しえないことを示すため

😊 Good

components/

|- TheHeading.vue
|- TheSidebar.vue
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

😰 Bad

components/

|- Heading.vue
|- MySidebar.vue
|- MyButton.vue
|- VueTable.vue
|- Icon.vue



密結合コンポーネントの名前

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

😊 Good

components/

|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue

😰 Bad

components/

|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue



コンポーネント名における単語の順番

コンポーネント名は一般的な単語から始まり、説明的な修飾語で終わるようにする。

例: 検索フォームのあるアプリケーション

😊 Good

components/

|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue

※ 100以上のコンポーネントがあるような場合のみSearchディレクトリの下にネストするのを推奨。

😰 Bad

components/

|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue



自己終了形式のコンポーネント

単一ファイルコンポーネント 、文字列テンプレート、およびJSXの中では自己終了形式で書くようにする。

※DOMテンプレート内は除く

😊 Good

<MyComponent />

😰 Bad

<MyComponent></MyComponent>



テンプレート内でのコンポーネント名の形式

単一ファイルコンポーネントと文字列テンプレート中のカスタムタグは、常にパスカルケース(PascalCase)にする。

※ DOM テンプレートの中ではケバブケース(kebab-case)であるべき。


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


😊 Good

<MyComponent />

😰 Bad

<mycomponent />

<myComponent />



JS/JSX 内でのコンポーネント名の形式

JS/JSX内でのコンポーネント名は、パスカルケース(PascalCase)にする。

😊 Good

Vue.component('MyComponent', {

// ...
})

import MyComponent from './MyComponent.vue'

export default {

name: 'MyComponent',
// ...
}

😰 Bad

Vue.component('myComponent', {

// ...
})

import myComponent from './myComponent.vue'

export default {

name: 'myComponent',
// ...
}

export default {

name: 'my-component',
// ...
}



完全な単語によるコンポーネント名

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

😊 Good

components/

|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

😰 Bad

components/

|- SdSettings.vue
|- UProfOpts.vue



プロパティ名の型式

プロパティ名は、定義の時はキャメルケース(camelCase)、

HTML(テンプレート)やJSXで使う際はケバブケース(kebab-case)にする。

😊 Good

props: {

greetingText: String
}

<WelcomeMessage greeting-text="hi"/>

😰 Bad

props: {

'greeting-text': String
}

<WelcomeMessage greetingText="hi"/>



複数の属性をもつ要素

複数の属性をもつ要素は、1行に1要素ずつ、複数の行に渡って書くようにする。

😊 Good

<img

src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
>

😰 Bad

<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">



テンプレート内での単純な式

複雑な式は、算出プロパティかメソッドに書くようにする。

コンポーネントのテンプレートには単純な式だけを含むようにする。

※テンプレートには、何が表示されるかを簡潔に記述する。

😊 Good

<!-- テンプレート内 -->

{{ normalizedFullName }}

// 複雑な式を算出プロパティに移動

computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}

😰 Bad

{{

fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}



引用符付きの属性値

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

😊 Good

<input type="text">

<AppSidebar :style="{width:sidebarWidth+'px'}">

😰 Bad

<input type=text>

<AppSidebar :style={width:sidebarWidth+'px'}>



ディレクティブの短縮記法

ディレクティブの短縮記法: @は、常に使うか、まったく使わないかのどちらかにする。

😊 Good

<input

:value="newTodoText"
:placeholder="newTodoInstructions"
>

<input

v-bind:value="newTodoText"
v-bind:placeholder="newTodoInstructions"
>

<input

@input="onInput"
@focus="onFocus"
>

<input

v-on:input="onInput"
v-on:focus="onFocus"
>

😰 Bad

<input

v-bind:value="newTodoText"
:placeholder="newTodoInstructions"
>

<input

v-on:input="onInput"
@focus="onFocus"
>


C. 推奨


コンポーネント/インスタンス オプション順序

下記の順序を推奨。

※一貫した順序を守ることで、プロパティが探しやすくなる。

 - el

- name
- parent
- functional
- delimiters
- comments
- components
- directives
- filters
- extends
- mixins
- inheritAttrs
- model
- props/propsData
- data
- computed
- watch
- ライフサイクルイベント (呼び出される順)
- methods
- template/render
- renderError



要素の属性の順序

下記の順序を推奨。

※一貫した順序を守ることで、カスタム属性とディレクティブが探しやすくなる。

 - is

- v-for
- v-if
- v-else-if
- v-else
- v-show
- v-cloak
- v-pre
- v-once
- id
- ref
- key
- slot
- v-model
- その他の属性
- v-on
- v-html
- v-text



コンポーネント/インスタンス オプションの空行

スクロールする程、長くなった場合はプロパティの間に空行を追加する。

😊 Good

props: {

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

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

label: String,
icon: String
},
computed: {
formattedValue: function () {
// ...
},

inputClasses: function () {
// ...
}
}



単一ファイルコンポーネントのトップレベルの属性の順序

単一ファイルコンポーネントでは、 <template><script><style> の順で書くようにする。

😊 Good

<template></template>

<sctipt></sctipt>
<style></style>

😰 Bad

<template></template>

<style></style>
<sctipt></sctipt>


D. 注意(危険)


keyを使わない v-if / v-if-else / v-else

v-if + v-elseと一緒にkeyを書かないと予期せぬ副作用が生じる。

副作用の例: https://jsfiddle.net/chrisvfritz/bh8fLeds/

keyは必ず書くようにする。

😊 Good

<div v-if="error" key="search-status">

Error: {{ error }}
</div>
<div v-else key="search-results">
{{ results }}
</div>

😰 Bad

<div v-if="error">

Error: {{ error }}
</div>
<div v-else>
{{ results }}
</div>



scoped付きの要素セレクタ

要素セレクタに直接CSSを書くのはやめましょう。

※CSSを書く際は、クラスセレクタを使うようにする。(パフォーマンス向上)

😊 Good

<style scoped>

.btn-close {
background-color: red;
}
</style>

😰 Bad

<style scoped>

button {
background-color: red;
}
</style>



暗黙的な親子間のやりとり

$parent$childrenは使わないようにする。

😊 Good

Vue.component('TodoItem', {

props: {
todo: {
type: Object,
required: true
}
},
template: `
<input
:value="todo.text"
@input="$emit('input', $event.target.value)"
>
`

})

Vue.component('TodoItem', {

props: {
todo: {
type: Object,
required: true
}
},
template: `
<span>
{{ todo.text }}
<button @click="$emit('delete')">
X
</button>
</span>
`

})

😰 Bad

Vue.component('TodoItem', {

props: {
todo: {
type: Object,
required: true
}
},
template: '<input v-model="todo.text">'
})

Vue.component('TodoItem', {

props: {
todo: {
type: Object,
required: true
}
},
methods: {
removeTodo () {
var vm = this
vm.$parent.todos = vm.$parent.todos.filter(function (todo) {
return todo.id !== vm.todo.id
})
}
},
template: `
<span>
{{ todo.text }}
<button @click="removeTodo">
X
</button>
</span>
`

})



Flux 以外の状態管理

グローバル状態管理には、this.$rootやグローバルイベントバスよりも、Vuexが推奨される。

Vuexを使うようにする。

😊 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)
}
}
}

<!-- 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>

😰 Bad

// main.js

new Vue({
data: {
todos: []
},
created: function () {
this.$on('remove-todo', this.removeTodo)
},
methods: {
removeTodo: function (todo) {
var todoIdToRemove = todo.id
this.todos = this.todos.filter(function (todo) {
return todo.id !== todoIdToRemove
})
}
}
})



まとめ


  • 全てを遵守する必要は無い


    • 個人的には A > B = D > Cの順で大事かなぁと思いました



  • チームでコーディングルールを採用する上での指標になる


参考 ( special thx )


さいごに

このアドベントカレンダーを通して、多くの人がVue.jsのスタイルガイドを知るきっかけになればと思います。

フライングメリークリスマス!🎄✨