先日、神戸三宮を中心に関西を盛り上げているVue.jsコミュニティ、**三宮.vueの勉強会に初めて参加して来ました。
そこで、「Vue.jsのスタイルガイドをちゃんと読もう!」**という発表に触発され、改めて自分でもスタイルガイドを読んでみました。
読んでみて、「当たり前ではないか!へのつっぱりはいらんですよ!」と思うことから、「へーー!そら知らなんだ!」ということもありましたが、どれも**「エラーを防止する」、「可読性を上げる」、「一貫性を持たせる」、「保守性を上げる」**という面で大事です。
実際にスタイルガイドを読むのが一番ですが、折角なので優先度Aの必須項目をメモとして書きました。
複数単語コンポーネント名を使う
ルートの
App
コンポーネントや、Vue が提供する<transition>
や<component>
のようなビルトインコンポーネントを除き、コンポーネント名は常に複数単語とするべきです。
Vue.component('todo-item', {
// ...
})
export default {
name: 'TodoItem',
// ...
}
これは、HTML要素の名前とコンポート名が衝突するのを防ぐのが目的です。
全ての HTML 要素は 1 単語となっているので、コンポート名として複数単語を使っているとHTMLの要素名と衝突することはないということです。
例えば todo
や Todo
というコンポート名をつけると将来 todo
というHTML要素が出て来たときに名前が被ってしまうので、コンポート名としては example1
や example2
のように todo-item
や TodoItem
というような名前にすると良いです。
コンポートのデータは関数でなければならない
コンポーネントで
data
プロパティを使用する際 (つまりnew Vue
以外のどこでも)、その値はオブジェクトを返す関数でなければなりません。
例えば、data
の値がオブジェクトの場合、それはコンポーネントの全てのインスタンスで共有されます。
export default {
data: {
listTitle: '',
todos: []
}
}
このように書き、このコンポーネントを再利用しようとした場合、コンポーネントの全てのインスタンスが同じデータオブジェクトを参照するので、1つのリストのタイトルを変えると、他のリストのタイトルも変わってしまいます。
そこで、各コンポーネントのインスタンスには自身のデータだけを管理してもらおうとすると、各インスタンスは一意のデータオブジェクトを生成する必要があります。
export default {
data: function () {
return {
listTitle: '',
todos: []
}
}
}
もしくは
export default {
data () {
return {
listTitle: '',
todos: []
}
}
}
このように、関数内でオブジェクトを返すと、各インスタンスが一意のデータオブジェクトを生成することができます。(※ ルートで直接オブジェクトを使うのは OK です)
プロパティの定義はできる限り詳細とする
コミットされたコード内で、プロパティの定義は常に少なくとも1つのタイプを指定し、できる限り詳細とするべきです。
export default {
props: {
status: {
type: String,
required: true,
}
}
}
type
や required
を明記することで、ミスを防いだり、エラーの原因の特定をし易くするという話ですね。
v-for に対して常に key を使用する
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
このように一意のキーを振りましょう。
もし、
<li
v-for="(item, index) in items"
:key="index"
>
とすると、キーの値が書き換わった場合に意図しない挙動に繋がる可能性があります。そのため、キーにはgood-example
のように一意のid
を設定するのが基本です。(参考: Vue.js: v-forで項目インデックスをkey属性にしていいのか)
v-for と一緒に v-if を使うのを避ける
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
good-example
の場合、v-if をコンテナ要素に置いているので、shouldShowUsers
が false
の場合は v-for
を評価しません。
もしこれを、
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
このように書くと、
v-forは v-if よりも優先度が高いので、このテンプレートは、
this.users.map(function (user) {
if (user.isActive) {
return user.name
}
})
と同様に評価されます。
アクティブユーザーが変更されたかどうかに関わらず、再レンダリングするたびにリスト全体を繰り返し処理する必要があります。
コンポーネントをスコープにする
アプリケーションにとって、トップレベルの
App
コンポーネントとレイアウトコンポーネント内のスタイルはグローバルかもしれませんが、他のすべてのコンポーネントは常にスコープされているべきです。
<template>
<button class="button button-close">X</button>
</template>
<style scoped>
.button {
border: none;
border-radius: 2px;
}
.button-close {
background-color: red;
}
</style>
上の例は style
に scoped
属性を使用しています。
これは、スタイルの汚染を防ごうということです。
scoped
属性を使わなくても、BEMなどを用いることで汚染を防ぐ方が良い場合もあります。
プライベートなプロパティ名を使う
プライベートな関数に外部からアクセスできないようにするために、モジュールスコープを使用してください。それが難しい場合は、プラグインやミックスインなどのプライベートなカスタムプロパティには常に
$_
プレフィックスを使用してください。さらに、他の著者によるコードとの衝突を避けるため、名前付きのスコープを含めてください (例$_yourPluginName_
)。
var myGreatMixin = {
// ...
methods: {
publicMethod() {
// ...
myPrivateFunction()
}
}
}
function myPrivateFunction() {
// ...
}
export default myGreatMixin
以上がVue.jsスタイルガイドの中で、優先順位が最も高い必須7項目です。
これまでちゃんと読んだことがなかったのですが、読んだ方が良いですね。
最初に書いたように、当たり前だと思うことも知らなかったことも書いてありますが、使う上での共通認識として理解しておくべきだと感じました。
本記事で紹介している優先度Aの必須7項目以外にも優先度B(強く推奨)、C(推奨)、D(使用注意)の項目があるので、是非スタイルガイドに目を通してみてください。
そう言えば、Nuxtを使ったときにESLintに Require self-closing on Vue.js custom components
や ”:value” should go before “〜”
のように言われることがありました。これらも、優先度Bの項目の自己終了形式のコンポーネント やコンポーネント名における単語の順番 に則ったルールに基づいているんですね。
知らなんだ。
三宮.vue 楽しかったので、関西にいる方は是非参加してみてください!