AngularJSからVueに切り替えながら学んだこと

プロローグ

今回はNHNが開発したグループウェアであるTOAST Dooray!を紹介したいと思います。プロジェクト、メール、メッセンジャー、カレンダー、ドライブ、Wiki、アドレス帳、カンバンボードなど、協業に必要な便利機能を多数盛り込んだサービスで、約5年前から開発を始め、現在まで維持しています。Dooray!は、プロジェクト初期に最も注目を集めていたAngularJSをフレームワークとして選択し、現在もAngularJSでほとんどのフロントエンド開発を行っています。しかし、AngularJSは1.7xバージョンで一旦終了し、次のフロントエンドのフレームワークとして、VueJSを採用することにしました。ここではフレームワークを切り替えながら学んだことを共有したいと思います。AngularJSは依然として高いシェアを保持しており、私たちと同じような悩みを抱えている会社や開発者が大勢いらっしゃると思います。フレームワークを切り替える機会があれば、参考にしていただけるとうれしいです。

フレームワークを切り替える前に、取り組むべきこと

フレームワークの切り替えは、新規機能を追加したり、バグを修正することがないため、切り替え後も以前のサービスと同じ機能を維持する必要があります。このとき必要になるのが回帰テストですが、既存の機能がうまく動作することをテストできる方法も事前に用意しておくとよいでしょう。

サービス企画書の確保

そのため、私たちが最初に探したものは企画書です。サービスを開発したプランナーと開発者がまだ一緒に仕事をしていたり、同じ会社で働いている場合は運が良いです。サービスの機能と履歴を引き継ぐことができるし、問題が生じても問い合わせができるからです。そして企画書も残っている確率が高い!

企画書にはサービスのほぼすべての機能仕様が定義されており、事前にこの仕様を把握しなければなりません。開発の進行手順や、実装にどの程度の工数がかかるか、大体の見当がつくからです。画面と機能の流れに沿って実装順序に依存性が生じるため、機能仕様の把握は非常に重要です。

Dooray!の検索機能において、このようなことがありました。
AngularJSで実装された検索領域で検索語を入力すると、Vue領域で検索結果を表示する機能仕様になっていることが、開発途中で判明しました。不足している機能を実装することになり、膨大なコードタイピングが必要になりました。

Dooray!アドレス帳のスクリーンショットとVue領域表示、緑の領域がAngularJSで実装された検索UIです。

QAテストケースのシート確保

QAのテストケースの確保も、フレームワークの切り替えを成功に導くために重要です。
企画書がない場合は、QAのテストケースが機能を把握する上で多いに参考になるからです。また、QAシートは速読せずに何度も精読しましょう。テストケースと実際のサービスの機能を1つ1つ照らし合わせながら、機能を十分に理解しましょう。

コードによる機能の把握

両方のドキュメントで機能の把握ができたとしても、コードベースで機能を把握することも必要です。コードを使って機能を把握する上で強調したいことは、UIライブラリーが変わる部分についてです。AngularJS基盤のライブラリでは、同じVueライブラリがあるか調べる必要があります。実装された一部コンポーネントは、継承やフォークなどの方法でカスタム実装したものかもしれません。

Dooray!アドレス帳はブートストラップを使用しており、Vueに移行する際は、element-uiを使用することにしました。UIライブラリーが異なるため、コンポーネントの機能とデザイン、詳細な動作(トランジションなど)も違ってきます。これによって開発が必要なものは、element-uiにサービス共通デザインを適用(cssオーバーライドを含む)、AngularJSでフォークやカスタム実装したコンポーネントやコードの再実装、element-uiに最適なコンポーネントがなく新規開発したコンポーネントなどがありました。
AngularJSからVueに切り替えた後、最も大変だったのが事前機能の把握でした。そのため、重要な企画書の確保、QAテストケースの確保、コードを用いた開発機能の把握は、非常に重要です。

開発計画を立てる(WBS作成)

開発方法論や管理方法、個人の性向などによって使用方法が千差万別なので、どのように計画を立案するかは言及しません。ただし、計画立案にあたって、既存の機能を最大限に把握することが重要です。

  • サービスを直接使ってみながら機能を把握する
  • 企画書を確保して機能を把握する
  • QAシートを見て把握する
  • コードを見て機能を把握する

開発計画を立てる際、予測と結果の格差を最大限に絞り込みましょう。

フレームワーク切り替え後の回帰テスト

スプリントが終わる頃、QAの前に開発者が回帰テストを行いました。もちろん、QAチームにFull QAを要請すればテストカバレッジが上がりますが、開発者の場合は本人が実装したコードなので、エッジケースをさらにテストできるのではないかと考えました。当初の目標は、成功率95%でしたが、実際に回帰テストをしてみると、成功率は85%でした。

その後、Full QAを4日間行い、検出された問題は70個を超えました。問題の原因と種類を分析すると、事前の機能把握が不足したことで発生した問題が約30%ありました。

フレームワーク切り替え後の性能テスト

フレームワークの切り替えを計画したとき、大きな悩みはパフォーマンスがどうなるかでした。Dooray!はSPAで開発されており、下図のように各サービスは上段にタブの形で表示されます。すべてのサービスを一度でVueに変換するには、難易度、スケジュール、メンテナンスの面で非常に困難な課題になるため、タブのうち、1つのサービスをVueに切り替えることにしました。

Webページの構造を決定

大きく3つの方法で検討しましたが、それぞれ長短がありました。これはパフォーマンスとも密接に関連しています。

最初の方法は、SPAを諦め、別のページに分離することでした。Vueを使って別ページで開発すれば済むので、大きな問題はないように思われました。既存の実装と新しい実装のCSSがうまく分離され、webpackを使って必要なファイルだけをバンドルするように処理できるので、第一の代案でした。欠点は、ページを切り替えるときに、ちらつきが出て、1つのアプリケーションに見えるSPAの利点が消えてしまうことでした。リソースを新たにロードする必要があり、パフォーマンスも遅くなりました。

2番目の方法は、SPAを維持しながら、Vue開発領域をiframeで処理することでした。最初の方法の利点もすべて収容し、プロトタイピングの結果、メインフレームと子フレーム間のメッセージを送受信する方法では、データ転送も大きく問題がありませんでした。最大の利点は、Shadow DOMのようにCSSを分離できることです。膨大なソースから予測困難なCSSの衝突を防ぐことができます。

3番目の方法も、SPAを維持しながら、AngularJSの内部にVueコンポーネントを直接挿入することでした。検索するとng-vueライブラリもありましたが、使用方法が若干難しく、原理を考えると直接実装した方が容易であると判断しました。AngularJSでは、特定のDOMサブはAngularJSコンパイルをせずに、純粋なDOMの形にしておけるng-non-bindableがあります。次のようにこのディレクティブを使うと、簡単にVueを挿入することができます。

AngularJSテンプレートのng-non-bindable

<div class="contact-vue-component" ng-non-bindable>
    <div class="vue-wrapper-element">
    </div>
</div>

AngularJSコンポーネントコントローラから直接Vueを生成する

this.$onInit = () => {
    this.component = new Vue({
        el: $element.find('.vue-wrapper-element')[0],
        store,
        router,
        components: {
            ServiceMain
        },
        data() {
            return {
                state: 'nice state'
            }
        },
        template: `
<service-main component-name="ContactBodyContainer" :state="state"></service-main>`
    });
};

Dooray!では3番目の方法を選択することにしました。徐々にVueコンポーネントが占める画面領域が広がり、AngularJSがフェードアウトされて、完全にVueに移行されることでしょう。

それで、性能は?

1番、2番の方法のように、別ページに分離していないため、従来のAngularJSで使用するすべてのリソースに、新規開発したVueで使用するすべてのリソースを追加する必要がありました。そのため、性能も低下するのではと推測しましたが、幸いなことに今回のフレームワークの切り替えによってパフォーマンスが遅くなることはありませんでした。
Chrome開発ツールのネットワーク、パフォーマンス、Auditsを使って、フレームワークの切り替え前後を測定しました。

ロード性能

これは、一部のロード性能測定データです。

イベント AngularJS AngularJS + Vue
DCL(DOMContentLoaded) 1900 ms 1922 ms
Loaded(L) 3856 ms 3666 ms
FMP(First Meaningful Paint) 5896 ms 3722 ms

DCLは、HTMLファイルの容量とJavaScript、CSSファイルの数に影響を受ける数値です。サーバー側のレンダリングがないSPAなので、HTMLファイルはサイズに変更がなく、JavaScriptとCSSのリソース構成の変化も性能に影響しないことが分かります。Loadedイベントもリソース構成にあるにも関わらず、性能が低下していませんでした。しかし、ローディング速度で重要な指標であるFMPは、2秒ほど速くなりました。[パフォーマンス]タブで、測定データを分析してみると、AngularJSのブートストラップ関数の呼び出し時間が、2秒程度減少したことが確認されました。AngularJSのブートストラップ時間が減り、Vueの初期化時間がそれに比べて非常に速いことが分かります。

全体的なローディング速度の面から見ると、リソースのローディングと処理ではパフォーマンスが低下しておらず、AngularJSのブートストラップ時間を減らすことで、FMPは2秒程度速くなり、ロード性能が改善されました。

AngularJSのパフォーマンス測定画面

AngularJS + Vueのパフォーマンス測定画面

リソース要請の比較

フレームワークを変更したことで、リソースの数と容量に変化がありました。主なリソースの要請数と容量のデータです。

  AngularJS AngularJS + Vue 備考
JSサイズ 11.7 MB 10.9 MB  
JSリソース数 29個 32個  
CSSサイズ 2.2 MB 3.0 MB  
CSSリソース数 10個 12個  
XHRリクエストサイズ 6.9 MB 5.6 MB API呼び出し1つ増加につき、1.2KB増える(開発者ツールのサービスウォーカーのJS、CSS、ダウンロードもXHRで記録されるため、APIベースで増加した数値が1.2KBである)
XHRリクエスト数 38個 37個 API呼び出しは従来と比べて1個増加

JavaScriptの容量が減ったことが一見理解できず、今回の配布がフレームワークの切り替え作業だけでしたので、意味のあるデータか判断するのが難しかったです。CSSも同様に、element-uiを使用したことで予想通り容量が若干増えました。XHRリクエスト数と容量は、意図した通り、ぴったり1つだけ増加しました。以前と比べて不要なXHRがなく実装されており、この点は満足いくものでした。

フレームワークを切り替えて

サービスのフレームワークを変更するには多くの努力が必要になります。実装も重要ですが、事前準備も非常に重要です。機能把握を確実に行って、不足している機能がないように実装しなければなりません。ユーザーの立場では、元々あった機能がなくなると、非常に不便に感じます。しかも新しい機能が追加されるわけではないので、ユーザーはフレームワークが変わったことすら気づかないでしょう。そして、AngularJS内部にVueを含めるWebページの構造上、パフォーマンスを十分に考慮して実装することも重要です。最後に、フレームワークの切り替えに必要な全体のフローを整理しましょう。

  • 事前の機能把握
  • サービス企画書の確保と精読
  • QAテストケースシートの確保と精読 + 直接テストしてみる
  • コードを使った機能の把握
  • 開発計画の立案
  • フレームワークの切り替え前後の性能測定

P.S. AngularJSとVueが同時に回帰するサービスを経験してみたい方は、Dooray!のアドレス帳とプロジェクトのカンバンボードを使ってみてください。無料体験もできます!

TOAST Meetup 編集部

TOASTの技術ナレッジやお得なイベント情報を発信していきます
pagetop