はじめに
こんにちは!
株式会社Blankrで業務委託のフロントエンドエンジニアとして働いている谷澤と申します。
弊社では、『ライドリ』というクリエイターの活動を支援するプラットフォームを運営しています。
私はこれまで複数の現場でフロントエンドの開発を行ってきましたが、その中でもライドリのフロントエンド開発はしやすく、フロントエンドコードの品質を担保するための様々な工夫がされています。
今回はその内容を紹介していきたいと思います!
目次
使っている技術
開発言語:Typescript, Go (Rest / GraphQL)
フレームワーク: Nuxt.js
インフラ: GCP, Firebase, Docker, Cloudflare
アーキテクチャ:
・バックエンド:CleanArchitecture
・フロントエンド:
コンポーネント:AtomicDesign
スタイリング:BEM
ライドリのフロントエンド品質管理のTips
コンポーネントの管理方法
独自のAtomicDesign
ライドリではAtomicDesignをベースにした独自の方法でコンポーネントを管理しています。
これにより、コンポーネントの再利用性が高まり、保守性も向上しています。
通常のAtomicDesignでは、
atoms
,molecules
,organisms
,templates
,pages
という5種類のディレクトリに分けて設計されると思いますが、
ライドリではcomponentsディレクトリの中で
primitive(共通のコンポーネント)
と domain(そのドメイン内で使われるコンポーネント)
というディレクトリに分けて
それぞれの配下に
atoms
,molecules
,compounds
,organisms
,templates
と5種類のディレクトリが存在しています。(Nuxtの設計の通り、pages
はcomponentsと同列に位置してます)
通常のAtomicDesignとは違い compounds
というディレクトリを追加しています。
compoundsとは
compoundsとorganismsが近い粒度のコンポーネントになりますが、その違いとしては外部依存を持つか、持たないかになります。
compounds = "外部に依存することなく、単独で機能するUIコンポーネントのグループ"
organisms = "外部依存を持つことができる複雑なUIコンポーネントのグループ"
ですので、例えば、graphqlのmutationを使うポイント購入決済用のコンポーネントは organisms
で作られており、
ただ選択している購入予定のポイント内容を画面に表示するためのコンポーネントであれば、compounds
で作られています。
AtomicDesignを採用するとどうしてもorganismsのコンポーネント数が多くなりすぎてしまう問題があると思います。
ライドリではそれが2つに適度の分けられていて、責務もわかりやすくなっています。
さらに、Lintの設定でAtomicDesignの規則に従って、atoms
に molecules
をimportしようとするとlintエラーが出るといったようにそれぞれの粒度でimportできるファイルを制限しています。
おかしな粒度でコンポーネントを作成してしまっていれば、importのlintエラーの時点でその間違いに気づけます。
また、atoms
,molecules
で作られたコンポーネントはFigmaと連携されています。
Figmaの中でコンポーネント名が表示されるので、参画して間もない方でもどのコンポーネントを使えばいいのかがひと目でわかります。
これによってデザインと実装の一貫性が保たれています。
スタイルの変数管理
ライドリではCSSフレームワークを使っていないので、デザインに合わせて全てのスタイリングを行う必要があります。
スタイリングはSCSSのBEM記法で記述しており、その時に variables.scss
ファイルで管理されている変数を使います。
この管理が徹底されているので、cssを書かないといけないといっても気持ちよくスムーズに書くことができます!
例えば、太文字タイトルの20pxの要素であれば、以下のように variables.scss
ファイルで定義されています。
@mixin title20px {
font-weight: 600;
font-size: $font-size-20;
line-height: $line-height-primary;
}
styleタグの中で以下のようにincludeして使うことができます。
@includes title20px
文字スタイルだけでなく、長文を省略するためのスタイル omitText
など、複数箇所で使われるであろう共通スタイルは全て変数管理されています。
@mixin omitText($lineNumber) {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: $lineNumber;
word-break: break-all;
}
また、これらの変数もFigmaと連携されているので、Figmaを見ればその箇所でどの変数を使えばいいかがわかります。
以上のようにそれぞれの開発者が無駄にスタイリングしなくて済むように工夫がされていてとても便利です。
pluginsの活用
Nuxt.jsのプラグインは、アプリケーションの初期化時に特定のコードを実行するための仕組みです。
この機能を活用して、いくつかの共通処理(ナビゲーションやエラーハンドリングなど)がpluginsに一元管理されています。
これにより、コードの重複を避け、コードの可読性と保守性が高まっています。
例えば、ナビゲーションの処理は plugins/01-navigator.ts
というファイルで管理されています。
vueファイル側でuseNuxtAppを使い、上記ファイルから呼び出した関数にroute名(nuxtのtypedRouterで型管理もされている)を渡すことで画面遷移できるようになっています。
useRouterを色々な箇所で呼びだすというようなことは行われていません。
また、ライドリではオンライン握手会といってファンと配信者がビデオ通話をできる機能があるのですが、そこで利用されるカメラデバイスのstate管理をpluginsに持たせる実装を最近行いました。
これによって、アプリとして保持しておきたい様々な箇所で使われるstateをpluginsの中に一元化することができました。
ロジック層の切り分け
ライドリでは、ロジック層を明確に分離し、GraphQLとTypeScriptでしっかりと型付けされています。
これにより、コードの予測可能性と安全性が高まり、バグの発生を抑えることができます。
index.vueと同列にlogin.tsというファイルを切り出し、GraphQLで取得したデータを整形する際のロジックや、mutationを行う際のロジックをこのファイルで記述しています。
そして、vueファイルにそのデータを渡す際にはTypeScriptで型付けをしているため、修正を行った後に予期していないデータが残っていたり、取得できていないデータを使用してしまっていたりするバグが起こらないようになっています。
共通機能のhooks化
フロントエンド開発の中で必ず共通化して使われるモーダルやローディングといったいくつかのコンポーネントは useModalDefiner
、useLoadingDefiner
としてFunctionalComponentでhooks内で定義しています。
例えば、共通のモーダルのコンポーネントとして、ModalBaseというvueファイルを作り、モーダルを表示するときはpropsにモーダルを表示するかどうかのフラグを渡す。
ということを行ったりすると思いますが、複数のモーダルを1ファイル内で使いたい場合、複数のフラグ、またはフラグのidを変更して各モーダルの表示を切り替えたりすると思います。
ライドリで使っているuseModalDefinerであれば、useModalDefinerでAインスタンス、Bインスタンスとそれぞれインスタンスを作成して、モーダルを表示したいときには Aインスタンス.openModal
といった形で関数を呼び出せば良いため、Propsとして渡すためのフラグを用意する必要がなく便利です。
こういった便利かつ共通化して皆が同じように使うことができる機能がライドリ内にはあります。
終わりに
以上、ライドリのフロントエンド開発のTipsを一部ご紹介いたしました。
ライドリのフロントエンド開発には効率的で高品質なコードを実現するための工夫がたくさん詰まっています。
自分自身ライドリで開発する中で勉強になることがたくさんありましたし、スキルアップできています。
しかし、まだまだやることできることはあります!
エンジニア募集中!
株式会社Blankrでは、ライドリの開発に携わっていただけるエンジニアを募集しています!
Go言語、Nuxt 3、GraphQLなどの実務経験がある方大歓迎です。
下記に各種リンクを記載していますので、ぜひご覧ください!
求人
正社員募集
業務委託募集
インタビュー記事
サービス