RAKSUL TechBlog

ラクスルグループのエンジニアが技術トピックを発信するブログです

SPA のコンポーネントをデスクトップ・モバイルで分割して開発した話

らくらくデザイン開発チームの波多野です。 今回は、今年の6月リリースしたらくらくデザインのモバイル対応について、フロントエンドではどのような設計方針で開発を行なったのかについて紹介します。

らくらくデザインとは

初めに、らくらくデザインというサービスについて紹介します。

らくらくデザインとは、チラシ・フライヤーを制作できる Webサービスです。ユーザは、「イベント」や「求人」といった用途ごとに好みのテンプレートを選び、写真の選択と最低限のテキスト入力をすることで、印刷に関する専門知識がなくてもチラシ・フライヤーを制作することができます。

[caption id="attachment_4700" align="alignnone" width="1024"]らくらくデザイン デスクトップ版の画面 デスクトップ版 らくらくデザインの画面[/caption]

らくらくデザインはこれまでデスクトップのみ利用可能でしたが、今回行なったリリースにより、モバイル(タブレット含む)端末での利用が可能になりました。

技術スタックについて

技術スタックについて簡単に紹介すると、らくらくデザインはサーバーサイドに Ruby on Rails、フロントエンドに Nuxt.js を用いた Single Page Application になっています。

フロントエンドについてもう少し記載すると、下記のような技術スタックを用いています。

  • Nuxt.js(with TSX)
  • emotion
  • Storybook

既存コンポーネントの設計について

モバイル対応をどのように行なったかについて解説する前に、以前のコンポーネント設計と管理方法について紹介します。らくらくデザインでは、下記のようなコンポーネント設計・管理方法を用いていました。

  • Atomic Design を導入している
    • Storybook によるコンポーネント管理
  • ロジックはコンポーネントから分離している

らくらくデザインでは、サービス開発当初から UI デザインの手法として Atomic Design を導入していました。下記は実際の UI デザインの XD データで、デザイナーによって Atomic Design の粒度でスタイルガイドが制作されています。

[caption id="attachment_4776" align="alignnone" width="1024"]XDで管理されたスタイルガイドの画面 XD 上でも Atomic Design の粒度でコンポーネント管理されている[/caption]

フロントエンドのコンポーネントも、Atomic Design の構造に基づいて設計されています。コンポーネントファイルは、UI 表示とロジックの責務が分離された状態で実装しています。この設計は、後述するモバイル対応のための設計に影響を与え、のちのコンポーネント開発が容易になりました。

また、これらのコンポーネントは Storybook で管理されており、主にデザイナーとのコミュニケーションや UI の動作・表示確認のために利用されています。

[caption id="attachment_4787" align="alignnone" width="1024"]Storybook の画面 Storybook によるコンポーネント管理[/caption]

モバイル端末への対応

これまでデスクトップ向けサービスとして稼働していたらくらくデザインですが、今年6月にモバイル端末でも利用可能になりました。この対応により、文字の「スタイル変更」や「文字のそろえ」といった一部の機能を除いて、デスクトップと同等の機能を利用することができます。

[caption id="attachment_4803" align="alignnone" width="616"]モバイル版 らくらくデザイン 画面 モバイル版 らくらくデザインの画面[/caption]

今回のモバイル対応では、レスポンシブデザインではなくユーザーエージェントによってデスクトップとモバイルとで表示するページを出し分けています。つまり、デスクトップとモバイルでそれぞれコンポーネントを持つように設計しています。その理由としては、下記の2つが挙げられました。

  • デスクトップとモバイルで UI の振る舞いが異なるため
  • 将来的にモバイルに特化した機能開発を行う可能性があるため

今回は、どのようにデスクトップとモバイルでコンポーネントを分ける判断をしたのか、またどのように分けたのかについて説明します。

デスクトップとモバイルでコンポーネントを分ける

例えば、らくらくデザインの場合はデスクトップとモバイルとでヘッダー UI は次のように異なります。

デスクトップ デスクトップのヘッダー
モバイル モバイルのヘッダー

 

これらはページ内の上部に表示されるという意味では、どちらも同じヘッダーというコンポーネントですが表示内容や機能が異なります。これは極端な例ですが、レスポンシブデザインでこれを実現しようとした場合、既存のコンポーネントファイルに手を加え、ユーザの画面に表示される情報よりも多くの状態とアクションを持ち、コンポーネントファイルが複雑化する恐れがありました。

そこで今回のモバイル対応では、コンポーネントを次のように分けました。

[caption id="attachment_4720" align="alignnone" width="300"]ディレクトリの画像 components ディレクトリ直下に common/desktop/mobile ディレクトリを追加[/caption]

desktopmobile はその名の通り、デスクトップ・モバイルそれぞれでしか使用しないコンポーネントを格納し、 common には、デスクトップ・モバイル共通で使用できるコンポーネントを格納します。前章でも述べましたが、既に UI とロジックが分離されているので、デスクトップ・モバイルでコンポーネントが異なる場合でもドメインロジックは共通で利用することができました。

共通コンポーネントの例として、デザインテンプレートを選択されたタグごとにフィルタリングしてくれる DesignFilterTag コンポーネントについて見てみましょう。

[caption id="attachment_4742" align="alignnone" width="300"]DesignFilterTag コンポーネント DesignFilterTag コンポーネントの UI[/caption]

このコンポーネントの場合、デスクトップとモバイルで width, heightfont-size などの CSS の値が異なりますので、props で値を受け取ることで共通コンポーネントとして再利用しています。DesignFilterTag.tsx は下記のようになります。(ソースコードは解説のために一部、修正・削除しています)

import { CreateElement, VNode } from 'vue';
import { Component, Prop } from 'nuxt-property-decorator';
import * as vts from 'vue-tsx-support';
import { css } from 'emotion';

// タグのレイアウトを整形するための CSS properties
export interface DesignFilterTagStyle {
  width: number;
  height: number;
  fontSize: number;
  radius: number;
}

export interface DesignFilterTagProps {
  label: string;
  style: DesignFilterTagStyle;
}

@Component
export class DesignFilterTag extends vts.Component<DesignFilterTagProps> {
  @Prop()
  label: string;

  @Prop()
  style: DesignFilterTagStyle;

  public render(h: CreateElement): VNode {
    return (
      <div class={this.rootClass}>
        {this.label}
      </div>
    );
  }

  private get rootClass() {
    return css`
      min-width: ${this.style.width}px;
      height: ${this.style.height}px;
      padding: 5px;
      background: #E9F9FC;
      border-radius: ${this.style.radius}px;
      font-size: ${this.style.fontSize}px;
      color: #00B2DA;
      text-align: center;
    `;
  }
}

このように、デスクトップまたはモバイルで必要に応じて値を受け渡すことで、共通コンポーネントとして再利用しています。

実現できたこと

デスクトップ・モバイルを別コンポーネントとして分けることで下記の3つが実現できました。

  • 既存の(デスクトップの)コンポーネントファイルに大きな変更を加えずに済んだ
  • コンポーネントファイルの見通しを保つことができた
  • モバイル(またはデスクトップ)のみの機能開発が容易である

本文でも触れましたが、モバイルではデスクトップの機能を一部ドロップしてリリースしています。限られたデバイスサイズの中では、機能が増えることで離脱(デザイン制作の妨げ)に繋がってしまう恐れがあるため、最小限の要件で開発を行いました。今回のリリースで、今後モバイルまたはデスクトップそれぞれのユーザにとって最適な体験を開発するための環境が整えられたように思います。

おわりに

6月にモバイル・タブレット対応を行い、9月にはチラシ・フライヤーに加えて、らくらくデザインで年賀状が制作できるようになりました。モバイル端末からも、年賀状が制作できますのでご利用ください。