GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

GameWithのリプレイスについて vol.2 ~Web Components を Vue で書いたら最高だった編~ #GameWith #TechWith

はじめに

こんにちは!Incremental Stream Team の@53able, @inosy22, @nog です!

前回のブログでは Gamewith で行っているリプレイスの概要について紹介しました。

tech.gamewith.co.jp

今回は Web Components を Vueで開発するための、システム( GameWithDesignSystem)を開発したので紹介したいと思います。

GameWithDesignSystem とは?

Vue で実装したコンポーネントを Web Components として配布をし、どんなプロジェクトでも利用できるコンポーネントを提供するシステムです。

実際に GameWith では下記のスクショのようにカウントダウンをする広告をGameWithDesignSystemで開発しました。

f:id:tiwu:20200416194434p:plain

GDS の目指す世界

  1. どんなプロジェクトでも簡単に利用できるHTMLコンポーネントの提供

  2. 将来的にデザイナーも UI/UX 改善に利用できるようにしたい

  3. 将来的にエコシステムを OSS として公開したい

作った背景

課題

リプレイスでは、Nuxt.js (Vue.js) を採用しています。

現在は BFF(Backends For Frontends)で Nuxt.js を動かして、SSRのみ行っています。(クライアントで Nuxt.js が動いているわけではありません)

リプレイスは段階的に行う計画のため、上記の対応によって既存のJSは後ほどまとめてリプレイスする予定でした。

しかし、既存のJSの部分的リプレイスを実施する必要が出てきました。

既存コードと新規コードを共存させる場合は、jQuery による DOM 操作と Vue で扱う仮想 DOM との相性が良いとは言えず、部分的にクライアントで Vue を取り入れるのは難しい状態でした。

部分的リプレイスの要件

  • 既存の jQuery で書かれている JS をモダンに実装
  • 将来的には完全な形で BFF にリプレイスする見込み
  • 既存のJSを Nuxt.js + TypeScript で実装し、BFFへ移行時に再利用
  • 既存のJSからの仮想 DOM の破壊を防ぐ

解決案

  • 仮想 DOM を破壊しない、ShadowDOM を採用
  • ShadowDOM を利用するために、Web Components を採用
  • SFC(Single File Component)を実装したあとに、Web Components へビルドできる環境

これまでリプレイスの BFF は Nuxt.js + TypeScript で実装してきていたので、技術的に要求されるギャップはなかったです。

また、SFCで実装してあれば、将来的にBFF(Nuxt.js)で再利用も可能です。

さらに、リプレイスチームだけでなく他のチームでも利用できるようにするため、リプレイスチームのプロジェクトに依存しない構成にしました。

技術構成

利用技術

  • Vue
  • VueCLI
  • @vue/web-component-wrapper
  • TypeScript
  • Storybook

@vue/web-component-wrapper

Vue コンポーネント をカスタム要素(Web Components)としてラップして登録します。

Vue 公式のラッパーです。

Composition API や Props や Vueの Life cycle method などの基本的な機能は、 Web Components にビルド後に問題なく動作します。

不要な Web Components のカスタム要素を生成しないようにするために、コンポーネント名は、prefix に _ で対応しました。Scss の仕組みを参考にしました。

vue-cli-service build --modern --target wc-async --inline-vue --name gds --dest 'dist/public' 'src/components/**/[!_]*.vue'"

@vue/web-component-wrapper

Storybook とどう連携するか

初期は、Storybook を導入していませんでした。

作成した Vue コンポーネントは App.vue に追加し、VueCLI の serve 機能を利用してブラウザに表示して確認しデバッグを行っていました。

しかし、そのままだと手間がかかる上に、リリースして実際に利用される Web Components での動作を常時確認したいので、カスタム要素を Storybook 上で管理できるようにしました。

Storybook では、利用するときに SFC をビルドしたコードを import する必要があります。

最初は新しいコンポーネントを追加する都度、該当するコードの import 文を書き換えてましたが、手間がかかっていたので下記コードでカスタム要素をすべてインポートするように自動化しました。

const fs = require("fs");
const scriptPath = "./dist/private";

// ファイル一覧からimportの文を作る
const makeIndexContent = files => {
  let content = "";
  files.forEach(item => {
    if (!item.endsWith(".min.js")) return;
    content += `import "./${item}";\n`;
  });
  return content;
};

// 非公開も含む全コンポーネントのindex.jsを作成
fs.readdir(scriptPath, (err, files) => {
  if (err) throw err;
  const content = makeIndexContent(files);
  fs.writeFile(`${scriptPath}/index.js`, content, err => {
    // eslint-disable-next-line no-console
    if (err !== null) console.log(err);
  });
});

コンポーネント開発をストレスなくやるために

  • stylelint-config-rational-order をStylelint に組み込んで、css プロパティの並び順を一律自動整形しています。

  • SFC を新規実装するときに、コマンドによってテンプレートからファイルを生成するようにして最低限必要なコードが揃っている状態から実装始められるようにしています。

リリースフロー

ビルドされたJSファイルをS3にアップロードします。

アップロード先は package.json の version を元にパスを切ることで、ブラウザキャシュのバグを防ぎ、カナリアリリースを実現させています。

利用したいバージョンのパスを指定して <script> タグで読み込むことによって、定義された Web Components のカスタム要素を利用できます。

利用する Web Components のJSだけが部分ロードされるので、コンポーネントの量が増えても問題ありません。

f:id:tiwu:20200416194503j:plain

苦労した点

  • ShadowRoot 内で document.* が使えずクッキーなどが取得できませんでした。

  • ビルドの監視や、 hot reload など行いたかったが、まだ対応できてないです。毎回全てのSFCをビルドしているので、確認まで数秒時間がかかってしまいますし、ビルド実行を自動化できてないです。

感想

SFC から Web Components へビルドし、利用できる方になるまでの過程で煩雑な作業をひとつひとつ解決してきており、 コンポーネント実装以外の工程は、設けているコマンドを実行することでまかなえるようしています。

初めてGDSを触る人にも易しい構成になっていきています。

独立したシステムで SFC を開発とテストすることは、配備される環境で考慮することが少なくて済むので、コアロジックに集中ができます。

しかし、SFC は Vue.js を前提に実装できることから容易にたくさんの仕様を詰め込んでしまうことが可能です。

コンポーネントの粒度は考慮する必要があります。

ただし、粒度が大きいコンポーネントを作ってしまっても、今回は TypeScript で実装しているので、後で分割するときにコードの整合性はコントロールしやすいと感じました。

レガシーな環境でも、モダンなコードで実装されたコンポーネントを追加できるので、今後は開発するのに継続的な効果が期待できそうです!

最後に

GDS の開発環境は、将来的には公開して利用できる様にしていきたいと考えているのでご期待下さい。

また、GameWith にイケてるコンポーネントがこれからたくさん作られていくので、ぜひ探してみてください!

GameWithのDeveloper向けTwitterアカウントも開設しました。
もくもく会の告知やブログの更新情報などを発信するので良かったらフォロー宜しくお願いします!

twitter.com