HITO-Link パフォーマンス の大幅リニューアルに伴いクライアントサイドは何が変わった?

こんにちは、HITO-Link 開発チームの楳田 (@wwwumeda) です。

10/2 に HITO-Link パフォーマンス の大幅リニューアルがありましたので、クライアントサイドが技術的にどう変わったかについて記載します。

prtimes.jp

はじめに

HITO-Link パフォーマンス は、初回リリースから約 2 年が経過しましたが、クライアントサイドの大幅改修が必要となりました。 変更前後の画面は以下のとおりです。

  • 新デザインの OKR 画面

    新デザイン

  • 新デザインの 1on1 画面

    新デザイン

  • 旧デザインのチェックイン画面

    • キャプチャーにありませんが OKR ツリーの下に 1on1 コメントが入力できました。
    • (キャプチャーだと マイページ がアクティブですが、実際は チェックイン がアクティブです。)

    旧デザイン

旧デザインでは OKR と 1on1 が 1 つの機能にまとめられており、個人の 1on1 のコメントと組織のコミットメントのコメントが混在するという問題がありましたが、新デザインではそれぞれが独立した機能となりました。

この変更に対して、既存の実装を流用するか新しく作り直すかを半年ほど前に議論しました。 結論としては、旧デザインでは以下のような問題があり、新しく作り直すことを選択しました。

  • HITO-Link パフォーマンスは SPA として作られており、クライアントサイドに多くの実装があるがユニットテストができていない。 ユニットテストができていないため、機能変更、リリース、仮説検証のサイクルが短期間でできない。
  • モバイル対応の要件がありレスポンシブ対応する必要があったが、旧デザインではできておらず、また、旧デザインをレスポンシブ対応するのは現実的でなかった。旧デザイン用の実装を維持しつつモバイル用の実装をするくらいなら、新しく作り直して PC 版とモバイル版を同じソースで実現したほうが開発効率がよい。
  • 試行錯誤しながら急いで開発していたところもあり、技術的負債が溜まっていた。これを解消したい。

この記事では、新旧の技術的な変更点や新しい取り組みについて記載していきます。 各取り込みの詳細については後日記載していければと思います。

Vue コンポーネントフレームワークの導入

新デザインでの最も大きな変更点は Vue コンポーネントフレームワークの導入だと思います。 HITO-Link パフォーマンスでは Vue.js を採用しており、これについては変更がありませんが、旧デザインでは CSS テンプレートや jQuery 用のデザインライブラリなどを導入しており、Vue.js の恩恵を受けきれていなかった印象です。Vue のライフサイクル内で Vue が作成した DOM を書き換えていました。

今回採用したフレームワークは Vuetify です。 Vuetify の採用理由は以下のとおりです。

  • マテリアルデザインを採用している。旧デザインでもマテリアルデザインを採用しているため、継続して適用した。
  • レスポンシブである。モバイル対応を最小工数で行うため、レスポンシブ対応のフレームワークである必要があった。これ系のフレームワークはだいたいレスポンシブでしょうが。
  • GitHub の Star 数が多い。継続してメンテナンスされている。モジュール選定は GitHub の Star 数を最初に見ることが多いですね。

結論としては、Vuetify の採用は正解で、シンプルで使いやすいと思います。 悩みどころは、Vuetify の問題ではありませんが、ブランド色を出しにくい、コンポーネントの CSS をどこまでどう上書きするか、でしょうか。

Vue のライブラリを検索する場合は以下のサイトが有効だと思います。

GitHub - vuejs/awesome-vue: 🎉 A curated list of awesome things related to Vue.js

脱 jQuery の実施

旧デザインでは jQuery を利用していましたが、新デザインでは UI コンポーネントのほとんどを Vuetify でカバーできるため、今のところは jQuery を利用していません。 脱 jQuery する目的は以下の 2 つが大きなところでしょうが、後者に焦点を当てたときにより脱 jQuery すべきと考えます。

  • ブラウザ依存の吸収が目的で利用していたが、ブラウザごとの差分が減ってきたため、jQuery を必要としない。
  • Vue、React、Angular などのフレームワークを利用しており、jQuery を必要としない。

前者は、実装方法が異なるだけなので、脱 jQuery をするメリットはファイルサイズの軽減くらいでしょうか。

後者に関してですが、jQuery を利用すると Vue などのフレームワークの管理外で DOM の構成を変えることになり、複雑さが増すので避けたほうがいいと思います。明示的に破棄処理が必要となったり、適用範囲(スコープ)を意識する必要があります。性能劣化につながるかもしれませんし、ユニットテストする上でも避けたほうが良いかもしれません。この辺は憶測ですが。

TypeScript の導入と脱 lodash

旧デザインでは JavaScript で開発していましたが、新デザインでは TypeScript を導入しました。コード補完が効きやすくなったり、開発中のミスに気づきやすくなりました。vue-property-decorator を利用して class コンポーネントの記法を取り入れたことも、コード補完が効きやすくなった理由の一つと思われます。

  • 旧バージョンの実装サンプル
<script>
  export default {
    data() {
      return {
        foo: 'value'
      };
    },
    methods: {
      bar() {}
    },
    created() {}
  };
</script>
  • 新バージョンの実装サンプル
<script lang="ts">
  import { Component, Vue } from 'vue-property-decorator';

  @Component
  export default class HelloWorld extends Vue {
    public foo = 'hoge';

    public bar(): void {}

    public created(): void {}
  }
</script>

脱 lodash と書きましたが、lodash は 便利関数の集まりなので、標準にはないが lodash で提供されている便利関数は今後も利用していくと思います。jQuery とは異なり完全に利用をやめる予定はありません。 ただ、不必要な利用シーンが多かったと思います。とりあえず lodash を利用しておけばエラーになりにくいのでそういった実装が多かった印象です。 TypeScript による型定義ができていれば、lodash の利用は削減できると思います。 利用削減によりビルド後のファイルサイズが軽減できます(ビルド構成次第)。

  • 不必要な利用例(_.forEach)
const foo = (): string[] | undefined => {
  return ['hoge1', 'hoge2'];
};
const bar = foo();

_.forEach(bar, str => {}); // これは利用しない。
bar && bar.forEach(str => {}); // 条件分岐を入れる。分岐がないと bar. ではコード補完が効かない。
  • 不必要な利用例(_.set)
const foo: { bar?: string } = {};
foo.bar = 'hoge';
foo.bar2 = 'hogehoge'; // エディタ上でエラーになる。
_.set(foo, 'bar2', 'hogehoge'); // エラーにならないため、ミスに気づくことができない。型定義に存在しないプロパティを作っている。(これくらいシンプルだと誰もこんな実装はしないでしょうが。)

想定外の状況でも落ちないように lodash を利用するのはありだと思います。 その辺はプロジェクトの規約として定義したほうがいよいのではないかと思います。

グローバルな CSS の撤廃

旧デザインでも webpack を利用したローカルな CSS で開発をしていましたが、グローバルな CSS も利用していました。

グローバルな CSS には以下のような問題があると言われています。

  • 使ってるかどうか不明な CSS が肥大化している。
  • 変更したときの影響範囲がわからない。

実際に、ある画面用に CSS を変更したときに別の画面で操作不能となるような致命的なバグも発生しました。 新デザインではほぼ全ての CSS を各 Vue コンポーネントに定義しており、別のコンポーネントに影響がないようにできています。

新しい取り組み   

クライアントサイドでのユニットテストの導入

旧デザインでも導入しようとしましたが、ユニットテストより画面を操作してテストしたほうが効率的、ユニットテストのメンテナンス工数が取れないことから導入を断念しました。 クライアントサイドのユニットテストで効果ができない一番の理由は、jQuery などで DOM を直接書き換える実装が原因ではないかと個人的に推測しています。

新デザインではほぼすべてのデザインが Vue の template と Model に応じて描画されるため、メソッドの INPUT と OUTPUT のテストが効果的となっており、ユニットテストの価値も上がったと感じています。

クライアントサイドでのユニットテストのカバレッジ率を OKR の KR として定義してみる

ユニットテストを導入して開発メンバーにテストするようお願いしてもなかなか浸透しないと思いましたので、OKR の KR にクライアントユニットテストのカバレッジ率(C0) 100% を上げました。 せっかく OKR 製品を作ってますので。

カバレッジ率を上げることがユニットテストの目的ではありませんが、クライアントサイドでのユニットテストが徹底できましたので、KR として定義したことは正解だったと思います。

新デザインリリース時点でのカバレッジ率は 73.8% でした。ゼロから作り直してのカバレッジ率としては妥当なところでしょうか。

Storybook の導入

Vue コンポーネントフレームワークの導入に合わせて Storybook も導入しましたが、こちらはあまり運用できていません。効果的な運用を模索中です。

旧デザインでは、デザインの規約系を PowerPoint で作っていましたが、存在が知られていなかったりメンテナンスされていなかったりしました。 Storybook で規約をまとめたらメンテナンスされ続けるのでは?、と考えてモバイル版の規約を作成しましたが、イマイチ運用できてないです。 PowerPoint よりはマシだと思うので、この辺は強い意志を持って Storybook を運用していくべきでしょう。

まとめ

大幅リニューアルに伴うクライアントサイドの変更点を記載しました。 技術的負債を解消し、クライアントサイドのユニットテストを徹底することで、品質を担保しつつ、より高速な変更・リリース・仮説検証のサイクルを目指していきます。

最後に、技術的負債の解消に理解を示してくださった PO やその周辺の方々に感謝です。