#概要
今回は、AtomicDesignでのコンポーネント設計をした行った時にやらかしたミスを記事にしてみました。
#AtomicDesignって
最初にAtomicDesignとはなんなのかを軽く振り返ってみましょう。
Qiita内外でも多くの説明がなされているので、ざっくりと。
AtomicDesignとは、コンポーネントを以下の5つの階層に分け、
コンポーネントの役割や責任を明確しそれらをルールを決めて分割していこう、という概念です。
- Atoms(原子)
- Molecules(分子)
- Organisms(有機体)
- Templates
- Pages
上が最小のコンポーネントで、下が一番大きいコンポーネントとなります。
また、AtomicDesignのルールとして、
- Atoms
- これ以上機能として分割できない機能達
- Molecules
- Atomsの集合体
- Organisms
- AtomsやMoleculesの集合体で、複雑な構成
- Templates
- MoleculesやOrganismsを配置する設計図
- Pages
- Templatesに対してデータを反映した物
といった感じですね。
それぞれの役割が明確化されていて、コンポーネントをどう作っていけばいいかの方針がわかりやすくなってますね。
それでもやらかした自分とは一体
#Atomsをつくってみた
まずは、設計がマズい形のAtomsを見てみましょう
今回はButton要素のコンポーネントを例に使います。
<template>
<button @click="onClick">
<slot></slot>
</button>
</template>
<script>
export default {
methods: {
onClick() {
return this.$emit("click");
}
}
};
</script>
<style lang="scss" scoped>
.Base__Button {
&--green {
//省略
}
&--red {
//省略
}
&--yellow {
//省略
}
}
</style>
HTML要素とScript要素は至ってシンプルな構成です。
CSS要素に関しては、1つのファイルの中に複数のClass属性が記述する形で作ってみました。
このComponentではclass属性の指定をせずに、Componentを配置した親側でClass属性を指定するという方法をとりました。
呼び出す側の例も書いておきます。
<base-button class="Base__Button--green">HOGE</base-button>
親でClass属性を指定してる時点でとっても嫌な予感がするぞ
#が、だめ!
最初はこの設計でも問題なく使えてましたが、使用するClass属性が増えて、記述が多くなってくると、
当然ながら使いづらくなってきました。
というのも、新しいスタイルが必要となった場合も同じファイルに追加していくために、
アイコンに使用する目的の無色透明で小さめのスタイルを…。
次はタブ用のスタイルを…
次はgreenだけ、ボタンのサイズを色々作りたい
次は…
となり、気づいたらClass属性の記述だけでファットになって、しかも複雑に絡み合い濃厚な味に…。
▂▅▇█▓▒░(‘ω’)░▒▓█▇▅▂ うわあああああああああ
###Badな設計
どのような点がBadであったかを確認してみましょう。
-
親と密結合
- 親でクラス名を指定しないと見た目を固定できない
- 下手したら親にスタイルのコードを書いている
-
ComponentのCSSがファット
- 1つのComponentに色んなスタイルを詰め込みすぎ
- CSSが上書きなどでスパゲッティコード化
-
Atomsの外でClass属性/CSS属性を記述
- コンポーネントの状態を閉じ込めれてない
基礎的な注意点ばかりではありますね…。
なぜ、このような設計のComponentを作ってしまったかも反省しておきましょう。
- 同じようなComponentを作るのはNG
- 1つのComponentで管理やったら楽じゃん!
- Atomsのルール通り処理の機能は分割しつつスタイルはまとめたい
- 色んな箇所で使うなら同じComponentのほうがいいのでは
- でも色んなデザインにしたいけど細かく制御できないから親にCSS属性を書くしか無い
というのを考えて作りやらかしてしまいました。
特に最後のは、もはやAtomsを親からスタイルを上書きしていたので、
これ普通のButtonタグでもよくない?のとこれ疎結合になっていないよね?という、色々なルール違反な状態となっていました。
#そうだ、いっぱいつくればいいんだ
では、どうすればいいんだ!と悩み、色々ググって情報を漁っていたところ気づきました。
色んな人のLT資料や、記事を見ていっそのこと振り切って考えていいのかもと思ってすえたどり着いた答えが、
そうだ、必要な分のAtomsをいっぱいつくればいいんだ
HTML要素やJavascriptと同じように、CSSも見た目毎に最小単位まで絞り込んだComponentを作ってしまえばいいのだと。
方針としては、「Importしたら見た目も完成していたそのまま使える」ぐらいの疎結合と独立感で。
早速、先程のButtonを作り直してみました。
ImportしてるComponentが多くてパス修正が大変だった
BaseButtonGreen.vue
BaseButtonRed.vue
BaseButtonYellow.vue
BaseButtonRadius.vue
BaseButtonSemiRadius.vue
分解し、それぞれ作ってみました。
中身の方は、試しにBaseButtonGreen.vue
を見てみましょう。
<template>
<button @click="onClick" class="Base__Button--green">
<slot></slot>
</button>
</template>
<script>
export default {
methods: {
onClick() {
return this.$emit("click");
}
}
};
</script>
<style lang="scss" scoped>
.Base__Button--green {
//省略
}
</style>
以前のComponentと違うのは、class属性をここで指定しCSSでは他のclass属性を記述していない、というところです。
BaseButtonGreen.vue
は名前で表すように緑色のボタンに関係するCSS属性だけを記述し、
BaseButtonRed.vue
は、赤色のボタンを。
BaseButtonRadius.vue
では、円形に関係するCSS属性だけを。
こうすることで、親とは疎結合になり独立性が高いComponentとなり、Importしそのまま使える事ができます。
###差分がある場合には?
BaseButtonGreen.vue
の縦横幅を大きい/小さいのを使いたいとなった場合ですが、
この場合はBaseButtonGreen.vue
の中に差分を書くのではなく、いっそのこと別Componentにしましょう。
BaseButtonGreen.vue
BaseButtonGreenLarge.vue
BaseButtonGreenSmall.vue
ファイル名に修飾子で「Large」か「Small」などを付け加えて、違いを明確化。
このように、同じ構成や見た目だけど細部が微妙に違うAtomsを作る場合には、ファイル名に修飾子をつけました。
サイズだけではなく、hoverなどの擬似クラスでちょっとリッチなエフェクトを付けたいけど、
エフェクトがないのも必要という場合には、Componentを2つに分割する事で変更内容を1ファイルに閉じ込める事ができます。
#おわり
今回はAtomicDesignで私がやらかした勘違いと失敗を、どのようにルールを見直した修正したかを記事にしてみました。
割とAtomicDesignの解説記事では、Moleculesからの作り方が多く書かれていますが、
Atomsでどうやって作るの?て記事はなかなか見ないので今回ネタにしてみました。
(ついでに自分犯した失敗の反省も込めて)
実際の現場ではAtomsは作らず、VuetifyなどのComponentFrameworkを使う事も多いと思われますが、
いざ自分でつくろうとすると案外どうやるんだったかな?とか、これでよかったよね?となりやすい部分であるので、
しっかりルール作りや方針確認をしたほうがいいですね!
また、アプリやサイトのデザインにがっつり影響する部分でもあるので
やっぱり自分で作ってると楽しいし、これからもたくさんAtomsを作っていこうかなと確信しました。
でも作りすぎるとImportがめんどくさいので、まとめて読み込む処理を組み入れなくちゃ…(確信)