はじめに
この度Vue.js(2系) + Vuetifyの構成(Laravelを使ったMPA)で社内で初めてVueを導入しての開発を行う事になったのですが、フロントエンドエンジニアが私一人ということもあり、コンポーネント設計で結構悩んだ話です。
そこで一応自分なりの結論が出たので、共有いたします。
まだまだ粗削りな部分はあるかと思いますが、少しでも皆さんの参考になれば幸いです。
詳細設計
まずVueに限らずコンポーネント志向のフレームワークでは基本的にAtomic Designをベースに設計すると思います。
そのため初めはそれに則り設計していたのですが、コンポーネントの粒度をどうするかの部分がイマイチ決まらず一度設計を見直す事になりました。
そこで、他の設計手法はないかと色々な企業様やエンジニアの方の記事を参考にさせていただきました。
※参考にさせていただいた記事
①Atomic Designが刺さる現場・刺さらない現場
②BASEのVue.jsコンポーネントの設計
③ワイ「何でそんな小っさいコンポーネント作ってるん?w
コンポーネント設計の概要については①、③の記事で勉強させていただき、詳しい設計は主に②のBASE様のコンポーネント設計を参考にさせていただきました。
まず上記②を参考にコンポーネントの種類は、
- 唯一http通信とstoreへのアクセスを行え、機能単位でのstateを持つベースとなるコンポーネント(Base)
- 表示にのみ責務をもつコンポーネント(Presentational)
- 様々なコンポーネントで再利用される、Presentationalコンポーネント(CommonPresentational)
の三種類としました。
5つに分かれているAtomic Designと比べると大分少ないと思います。
ただ根本的な思想の部分は同じで、まず「Pages」と「Templates」に相当するのがBaseです。
上記の通り機能単位の比較的大きなstateを持ち、唯一http通信とstoreへのアクセスが行えます。
次に「Organisms」と「Molecules」と「Atoms」に相当する、表示にのみ責務を持つのがPresentationalになります。
その中から再利用するものはCommonPresentationalに切り出し、あとは「Atoms」や「Molecules」単位で切り出す事を強制せず、必要に応じて新たなPresentationalを作るか、コンポーネントに直接記述します。
そしてコンポーネントの命名規則ですが、Baseには頭にbase-
をつけ、Presentationalは親のBaseのbase-
以降の文字を受け継ぎます。
例: base-header
> header-bar
, header-drawer
またCommonPresentationalは頭にcommon-
を付ける事としました。
例: common-breadcrumbs
コード
ではコードの面で具体的にはどうなっているかですが、以下のサンプルページでの例をご覧ください。
-サンプルページ-
まずは全体の構成。
(※本来base-header
は全ページ共通なのでapp.blade.php
に記述し、base-contact
はルーティングで切り替える部分なので実際の構成とは異なっており、レイアウトやデータの受け渡し部分も分かりやすくするためにあえて端折っています。)
<template>
<div id="app">
<base-header />
<base-contact />
</div>
</template>
次にbase-header
の中身は、
<template>
<header class="header">
<header-bar />
<header-drawer />
</header>
</template>
最後にbase-contact
の中身は、
<template>
<div class="contact">
<common-breadcumbs />
<contact-form />
<div>
</template>
のようになっています。
ここで登場するコンポーネントは全部で6つで、画面とともに色分けしたものが以下です。
- Base
- base-header
- base-contact
- Presentational
- header-bar
- header-drawer
- contact-form
- CommonPresentational
- common-breadcrumbs
※コード量が少なく再利用もしない場合は、更にcontact-form
内をパーツ単位でPresentationalに切り出すことはせず、コンポーネントに直接記述する。
このような感じで、機能単位でコンポーネントを分けています。
まとめ
いかがでしたでしょうか。
言ってしまえばAtomic Designの縛りを緩くして、「通信・storeアクセス・state保持用のコンポーネント」・「表示用のコンポーネント」・「表示用の共通コンポーネント」の3つに分けただけなのですが、今のところうまくハマっているのでやり直してよかったです。
それでは。