Vue Fes Japan 2024 セッションレポート「Vue.js、Nuxtの機能を使い、大量のコピペコードをリファクタリングする」を聞いて、コンポーネントについて考えてみた - giftee Tech Blog

giftee Tech Blog

ギフティの開発を支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信しています。

Vue Fes Japan 2024 セッションレポート「Vue.js、Nuxtの機能を使い、大量のコピペコードをリファクタリングする」を聞いて、コンポーネントについて考えてみた

eye_cache

こんにちは、エンジニアの toki (@tokai235) です。法人向け eGift サービス giftee for Business の開発をしています。普段はバックエンドやインフラのお仕事が多いですが、フロントエンドエンジニアを自称しています。

先日開催された Vue Fes Japan 2024 に参加してきまして、色々と面白いセッションやパーティを楽しんできました! 会場でお話いただいたみなさま、ギフティブースに立ち寄っていただいたみなさま、ありがとうございました!

その中でも印象に残ったセッションがあったので、自分の所感も交えながらご紹介できればと思います。

セッション 「Vue.js、Nuxt の機能を使い、大量のコピペコードをリファクタリングする」 について

お話したいセッションは いがにん (@igayamaguchi) さんによる「Vue.js、Nuxt の機能を使い、大量のコピペコードをリファクタリングする」です。

ご本人が登壇資料をあげておられるので、詳しく知りたい方はこちらをご覧ください!

じゃあこの記事では何を話すの?となるわけなんですが、この記事では

  • セッションの内容をざっとかいつまみつつ
  • ギフティではどうなの?

という話をできればと思ってます。

セッションはこんなお話でした

一休さんのプロダクト一休ではコピペコードがたくさんあり、その多くは似たような UI コンポーネントの再実装が繰り返されていることに因っていたそうです。

そこで一休さんでは、以下の 3 つを行なうことでコピペコードが大幅に減らせたそうです。

  1. プロダクト横断レベルのコンポーネントはデザインシステム作成
  2. プロダクト特有のコンポーネントはまずデザイン統一を進めてコンポーネント化
  3. コンポーネント横断のロジックは CompositionAPI で切り出し

ギフティではどうなの?

これに対してギフティではどうなの?というところを、セッションで取り上げられていた原因別に沿ってお話しできればと思います。

1. 汎用的な UI 要素

複数プロダクトで同じようなコンポーネントを再生産している、という問題についてはギフティでも同様に課題となっていました。特にギフティは過去の記事:「AWS アカウントの合計が 150 を超えました」でもご紹介したように、非常に多くのプロダクトがあります。(ちなみにこれは記事中でも触れられていますが、ギフティのエンジニアリング組織は「自己組織的」であることを大事にしているところから来ています。)

そこでギフティでもデザインシステムを作ることによって、汎用的な UI 要素をプロダクトの外で一括管理し、それを複数のプロダクトで利用しています。

ギフティのデザインシステムについては、開発に携わっている egurinko さんがいくつか記事を書いてくれているので、興味のある方は読んでみてください!

汎用 UI 要素の責務はどこまで?

余談ですが汎用 UI コンポーネントを作る際にどこまでをコンポーネントの境界とするか、悩んだことのある方は多いのではないでしょうか?

これに関しては基本的には「コンポーネントのスタイルやギミックまでを汎用 UI に含め、それによるデータの更新や加工はドメインロジックとしてコンポーネントの外に持つ」がいいのかなと思っています。

また MUIChakra UI などの UI ライブラリパッケージで提供されているコンポーネントが非常に洗練されており、参考にするのも良いと思います。

例えば MUI の Button コンポーネント を見てみると、disabledvariant などのボタン自体のスタイルやギミックは具体的な値を指定できる一方で、クリックしたときの挙動などは onClick で関数を渡せるようになっています。

つまりボタンというパーツに求める動き自体はコンポーネント内で責務を持つものの、クリックして何が起こるかは呼び出し元に委ねる、という責務分解がなされているわけですね。

2. プロダクト特有のコンポーネント

これに関しては一休さんとやっていることはおおよそ同じで、

  • デザインに統一感を持たせる
  • コンポーネントを分割する

を愚直にやっていっています。

特にデザインの統一はデザイナーとエンジニアの協働が不可欠です。giftee Tech Blog でもたびたび話題に上がるトピックなので、興味がある方は記事を読んでみてください!

3. 状態が絡んだロジック

私が開発している giftee for Business では Next.js を採用しているのですが、基本的な考え方は Nuxt と同じで、コンポーネントとロジックをどう扱うかという話になると思います。

実は giftee for Business は 一休.com/Yahoo!トラベルとは逆の道を進んでおり、もともとコンポーネントとロジックを分割していましたが、現在は寄せるようにしています。

簡単なディレクトリ構成で説明すると以下のようなイメージです。(Next.js の app router を想定しています。)

before

app
├── components
│   ├── AAA
│   │   └── index.tsx
│   └── ...
├── pathXXX
│   └── page.tsx
├── lib
│   ├── logics
│   │   └── logic1.ts # component と独立して logic が作られる
│   ├── utils
│   └── ...
└── ...

after

app
├── components
│   ├── AAA
│   │   ├── logic.ts # component ごとに logic が作られる
│   │   └── index.tsx
│   └── ...
├── pathXXX
│   └── page.tsx
├── lib
│   ├── utils
│   └── ...
└── ...

なぜこういう形になったかというと、Next.js で実装されている Partial Pre-Rendering (PPR) や Astro で採用されている Islands Architecture といった新たなレンダリング手法の台頭が大きいです。

これらの手法は詳細は色々と異なるのですが、思想としては、ページ単位のレンダリングではなくコンポーネントがそれぞれ独立してレンダリングされることで最適化を図るものです。

nextjs_ppr Next.js 公式 Docs より引用

astro_islands Astro 公式 Docs より引用

この世界ではコンポーネント単位でレンダリングが管理されるので、それに伴い実装方法なども違ってきますし、レンダリングされるタイミングもバラバラです。こうなってくるとむしろコンポーネントがロジックも含めて「完成品」として管理されたほうが、複雑にならずに済むと考えています。

もちろん複数のコンポーネントで使いたい utils もあるので lib のようなディレクトリがなくなることはありませんが、コンポーネントが持つロジックに比べると薄く、汎用的なものに限定されています。

このあたりのお話については非常に詳しくまとめてくれている記事など公開されていますので、そちらも参考にしてもらえると良いと思います!

PPR - pre-rendering 新時代の到来と SSR/SSG 論争の終焉

おわりに

コンポーネントの管理をどうするかという問題は多くのフロントエンドエンジニアの関心事かと思いますが、大きなトピックであるがゆえに非常に多くの事例や手法があり、自分にあったものを探すのはとても難しい問題だと思います。

今回の記事では一休さんとギフティでの取り組みについてご紹介しましたが、もし参考になれば嬉しいなと思います。

ギフティではフロントエンドエンジニアを募集しています。デザインシステムに興味がある方も、コンポーネントの管理に関心がある方も、ぜひカジュアル面談などでお話ししましょう。

We Are Hiring!