
デザイナーが考えるVue.jsでのコンポーネント設計
こんな方が対象
- デザイナー
- HTMLは作成したことがあるがUI系のフレームワークはvueが初めて
- チーム開発をしているが、エンジニアとの分業が難しいと感じる
- これからvueをチームで開発したいと思ってる
はじめに
私は、webデザイナー兼ディレクターとしてフリーランスで仕事をしています。
基本的には、プロジェクト単位でWebディレクションのご依頼いただいたり、Webサイトの施策提案を行い、Webデザインから制作まで行わせて頂くことが大半です。
プロジェクトローンチまでのディレクションになると、長期に渡り携われない事が多くなってしまいます。その為「運用のしやすさ」を考慮した上で機能やワークフローを設計していく事が多くあります。
今回、vueによる開発案件で途中JOINした際に、デザイナー、エンジニアの棲み分けを明確にし、メンバーのレベルに応じたコンポーネント設計を行った方が良いと実感しましたので、もし、今同じ様な状況にあるチームの方や、これからvue.jsによる開発を行いたいデザイナーさんがいらっしゃいましたら、参考にしていただけたら幸いです。
vue.jsとは
vue.js(ヴュージェイエス)とはJavaScriptのフレームワークです。
JavaScriptのフレームワークではjQueryが有名ですので一度は聞いたことがあるかと思います。
vue.jsはUI(ユーザーインターフェース)を構築する事に特化したフレームワークです。
同じUI構築に特化したフレームワークにAngular、Reactなどがありますが、vueは後発ながら他のフレームワークの良いとろを取り入れており人気の高いフレームワークです。
個人レベルからチーム開発まで幅広く使うことができます。
vueの個人開発とチーム開発の違い
個人開発であれば、自分で運用していくのでコンポーネント設計をしっかり行わなくても破綻をきたす事は無いと思います。
また、Vuetify、BootstrapVue、Buefy、Quasar Framework、Vue Material、Mint等のUIフレームワークをそのまま利用すれば比較的簡単に構築できると思います。
しかし、Webサービスの開発や、チーム開発となると、そもそもUIフレームワークを導入するのか、導入するならどの様な導入コストが発生するかを考慮しなければなりません。
特にコンポーネントは、デザイナー、エンジニア双方が関わる事になるので必ず最初に双方合意を取って進めた方が良いでしょう。
そうしないと、簡単な修正でもエンジニアが修正しなければならない、コンポーネントが再利用できないなど、vueの恩恵を全く受けれない物ができてしまいます。
デザイナー視点でのコンポーネントの考え方
vueでのコンポーネント設計をちゃんと行った方が良い理由としては、デザイナー、エンジニア双方に触る事になるからです。
例えば、簡単なformのUIをデザイナーが作成したとしましょう。そこにエンジニアがコンポーネント内でAPIを読み込む処理を記述した場合、デザイナーはアラート表示のデザインを追加できるでしょうか?
勿論vue.jsに詳しいデザイナーであればエラー判定のjavascriptを記述しエラーの場合は特定のclassを追加する。jsを追加できるでしょう。
しかし、複数のエンジニア、デザイナーが同じスキルを身につけるのは難しいのではないでしょうか?
そこで、エンジニアの範囲とデザイナーの範囲を明確に分ける事にしました。
アトミックデザインとコンポーネント
アトミックデザインとは、ペンシルバニア州の南西部ピッツバーグを拠点に活動するBrad Frost氏によって提唱されたデザインシステムを作成するための方法論です。
UIコンポーネントを5段階のレベルに分類し、組み立てることで
- コンポーネントを再利用できるようになる
- エンジニアと並行開発が可能になる
- コンポーネント単位でテストができる
- メンテナンスが少なくなる
- デザインの実装作業が短くなる
などのメリットが生まれます。
詳しい概念は、こちらで読むことができます。
コンポーネントは5段階のレベル(Atom、Molecules、Organisms、Templates、Pages)が用意されていますが、デザイナーがメインで携わるのはAtomレベルになると思います。
逆に言うと、Atomレベルのコンポーネントがしっかり作られていれば、デザイナーはほとんど触る必要がなくなってくると思います。
それに気づいたのは、Presentational ComponentとContainer Componentという分類パターンです。
簡単に説明すると、Presentational Componentは見た目に関する責任を持ち、propsで受け取った値を表示する。一方Container Componentはロジックや振る舞いを定義します。
そこでデザイナーはPresentational Component、エンジニアはContainer Componentに対して責任を持つ事にしたところ、かなりスムーズに開発が動く様になりました。
また、並行してディレクトリもAtomレベルのコンポーネントのみディレクトリを変えた事で、コンポーネントが見つけ安くなるなど、想像よりも沢山の恩恵をうけることができました。
ここで1点注意があり、アトミックデザインに書かれた事を全て正だと思わない方が良と思います。
確かにアトミックデザインは綺麗にコンポーネントのレベルが分けられています。しかしアトミックデザインには運用という視点が入っていません。
多くの人はアトミックデザインのレベル通りにコンポーネントを作れば良いだろと思うでしょう。
しかし、実際webサービスに導入した場合、スプリントを数多く回すためにはそんなにコンポーネントをレベルごとに綺麗に作ってられない場合もあります。作ったコンポーネント自体近いうちに消えることもありますから。
アトミックデザインは理解しつつも、状況により関係する人、ビジネスの視点からアレンジしていく事は重要だと思います。
これだけは覚えたいVue記述
v-bind
v-bindは様々なHTMLの属性を使うことができます。classや、画像のパスなどを設定することができます。
※ページ下部のサンプルでは、classの設定でv-bindを使用しています。
※v-bindは「:」と省略することが可能ですが、ページ下部のサンプルでは、わかりやすくする為、省略はしていません。
props
propsは親コンポーネントから子コポーネントに、文字、数値、配列、オブジェクトを渡すことができます。
※ページ下部のサンプルでは、親コンポーネントにclassを渡しています。
slot
slotは<template>タグ内の文字列をslotタグと置き換えます。
※ページ下部のサンプルでは、親コンポーネントで<template></template>タグ内に記述した文字列を子コンポーネントのボタンのラベリング名に置き換えています。
子コンポーネント例(Button.vue)
<template>
<button
type="button"
v-bind:disbled="btnDisabled"
v-bind:class="classObject"
@click="click"
ontouchstart=""
>
<slot></slot>
</button>
</template>
<script>
export default {
name: 'Button',
data: function() {
return {
btnDisabled: false,
}
},
props: {
size: {
type: String,
require: true
},
color: {
type: String,
require: true
},
btnDisabled: {
type: Boolean,
default: false
}
},
computed: {
classObject: function () {
return {
small: this.size === "small",
large: this.size === "large",
primary: this.color === "primary",
secondary: this.color === "secondary",
}
}
},
methods: {
click() {
this.$emit("click");
this.btnDisabled = true;
}
}
}
</script>
<style scoped lang="scss">
button {
display: inline-block;
font-weight: bind;
color: #fff;
background-color: #bbb;
border-color: #bbb;
text-align: center;
border: 1px solid transparent;
padding: 10px 20px;
font-size: 1.25rem;
line-height: 1.5;
border-radius: .25rem;
}
.small {
font-size:1rem;
padding: 7px 10px;
}
.large {
font-size:1.5rem;
padding: 15px 20px;
}
.primary {
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
.secondary {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
</style>
親コンポーネント例(App.vue)
<template>
<div id="app">
<Button>初期ボタン</Button>
<Button color="primary">primaryボタン</Button>
<Button color="secondary">secondaryボタン</Button>
<Button size="small">小さいボタン</Button>
<Button size="large">大きいボタン</Button>
</div>
</template>
<script>
import Button from '@/components/basics/Button.vue'
export default {
name: 'App',
components: {
Button
}
}
</script>
</style>