Angular 1 アプリの Angular 2 へのアップグレードをお考えですか?今日はインクリメンタル なアップグレードについてご紹介します。
概要
朗報です!
- 同じアプリケーションで Angular 1 と Angular 2 を混在させることができます。
- 同じビューに Angular 1 と Angular 2 のコンポーネントを混在させることができます。
- Angular 1 と Angular 2 ではフレームワークをまたがってサービスを提供することができます。
- フレームワークをまたがってデータ バインディングが動作します。
アップグレードする理由
Angular 2 には、大幅なパフォーマンスの向上、より強力なテンプレート、遅延読み込み、シンプルな API、容易なデバッグ、高度なテストが可能など、Angular 1 よりも多くのメリットがあります。その主な例をご紹介しましょう。
パフォーマンスの向上
アプリの動作が軽快になるように、数多くのシナリオを検討しました。当初に比べると、レンダリングや再レンダリングの速度は 3~5 倍です。
- Monomorphic な(単相的な) JS 呼び出しによる変更検出の高速化
- テンプレートのプリコンパイルと再利用
- 表示のキャッシュ
- メモリ使用率 / VM の負荷の低減
- Observable(監視可能)、もしくは Immutable(不変)なデータ構造による線形(超高速)な拡張性
- Dependency Injection(依存性の注入)でインクリメンタルな読み込みをサポート
より強力なテンプレート
- 多数のディレクティブの必要性を排除
- 静的分析が可能:将来のツールや IDE では、実行時ではなく開発時にエラーを検出できる
- テンプレートの作成者がディレクティブ定義で手書きする代わりに、バインディングの使用方法を決定できる
将来の可能性
DOM から Angular 2 のレンダリングを切り離しました。我々は現在、この切り離しによって実現する次のような機能のサポートについても積極的に取り組んでいます。
- サーバー側でのレンダリング。超高速な初期レンダリングとウェブクローラーのサポートを実現します。
- Web Worker。お使いのアプリやほとんどの Angular を Web Worker スレッドに移動して、常に UI がスムーズでいつでも応答できるようにします。
- ネイティブ モバイル UI。Google はモバイル アプリでの Web プラットフォーム サポートに注力しています。同時に、一部のチームは iOS と Android のモバイル アプリで完全にネイティブな UI を提供しようと取り組んでいます。
- ビルド ステップとしてのコンパイル。Angular アプリは HTML テンプレートを解析してコンパイルします。コンパイル ステップをビルド プロセスに移すことによって、初期レンダリングの高速化に取り組んでいます。
Angular 1 および 2 を同時に実行
Angular 2 はパフォーマンス、簡易性、柔軟性において Angular 1 よりも大幅に向上しています。Angular 2 のコンポーネントやサービスを単一のアプリにシームレスに取り込めるようにすることで、既存の Angular 1 アプリケーションのメリットを簡単に活かせるよう取り組んでいます。これによって、多数の小規模なコミットに対してアプリケーションの 1 つのサービスまたはコンポーネントを同時にアップグレードできるようになります。
たとえば、下の図のようなアプリがあるとします。Angular 2 の使用を開始するには、左側のナビゲーションを Angular 2 コンポーネントにアップグレードするように指定します。自信が付いたら、メイン コンテンツ エリアのスクローリング エリアに Angular 2 のレンダリング速度を利用するように指定します。
これを実現するには、Angular 1 と Angular 2 の間で 4 つの連携を行います。
- 依存性の注入
- コンポーネントのネスティング
- トランスクルージョン
- 変更の検出
これらすべてを可能にするために、我々は現在、ng-upgrade という名前のライブラリの開発を進めています。既存の Angular 1 アプリケーションに ng-upgrade と Angular 2 をインクルードすることで、思いのままに組み合わせることができるようになります。
詳細と擬似コードについては、最初の
アップグレードの設計に関するドキュメントを参照してください。また、この動作の概要についても記載されています。今後の投稿で、Angular 1 コードの Angular 2 へのアップグレードの具体例をご紹介していきます。
依存性の注入
最初に、アプリケーションのパーツ間のコミュニケーションを解決する必要があります。Angular における、別クラスまたは関数の呼び出しの最も一般的なパターンは、依存性の注入(Dependency Injection)です。Angular 1 には 1 つのルート インジェクターがあり、Angular 2 には
階層型のインジェクターがあります。一度に 1 つのサービスをアップグレードするということは、2 つのインジェクターが互いにインスタンスを提供できるようにする必要があることを意味します。
ng-upgrade
ライブラリによって、Angular 1 のすべてのインジェクターが自動的に Angular 2 で利用できるようになります。これは Angular 1 のアプリケーション サービスを Angular 2 のあらゆるコンポーネントまたはサービスに注入できるようになったことを意味します。
Angular 1 のインジェクターへの Angular 2 のサービスの公開もサポートされますが、マッピング構成を単純にする必要があります。
その結果、独立したコミットに対して一度に 1 つのサービスを Angular 1 から Angular 2 に簡単に移行し、混在環境でのコミュニケーションを実現できます。
コンポーネントのネスティングとトランスクルージョン
いずれのバージョンの Angular でも、コンポーネントは独自のテンプレートを持ったディレクティブとして定義しています。インクリメンタルな移行については、これらのコンポーネントを一度に 1 つずつ移行できるようにする必要があります。これは、ng-upgrade で各フレームワークのコンポーネントが互いにネストできる必要があることを意味します。
これを解決するため、ng-upgrade が Angular 1 コンポーネントを
ファサードにラップできるようにすることで、これらを Angular 2 コンポーネント内で使用できるようにしています。逆に、Angular 2 コンポーネントを Angular 1 で使用するためにラップすることもできます。これは、Angular 2 のコンテンツ プロジェクション同様、Angular 1 のトランスクルージョンでも完全に動作します。
このネストされたコンポーネントでは、各テンプレートが Angular 1 または Angular 2 のいずれかに完全に所有され、その構文およびセマンティックに厳密に従います。これはエミュレーションではなく、コンポーネントのタイプに応じた各フレームワークで実際に動作します。つまり、Angular 2 にアップグレードされるコンポーネントは、構文が改良されるだけではなく、Angular 2 のすべてのメリットを享受できるのです。
また、Angular 1 のコンポーネントはAngular 2 のテンプレートで使用される場合でも常に Angular 1 の依存性の注入を使用し、Angular 2 のコンポーネントは Angular 1 のテンプレートで使用される場合でも、常に Angular 2 の依存性の注入を使用することも意味します。
変更の検出
Angular 1 と Angular 2 のコンポーネントの混在は、Angular 1 のスコープと Angular 2 のコンポーネントが交互に配置されることを意味します。このため、ng-upgrade では、式の予測可能な評価順を維持するため、変更の検出(Angular 1 のスコープ ダイジェストと Angular 2 の変更の検出機能)が同様に交互に配置されます。
ng-upgrade
ではこれを考慮して、Angular 1 のスコープ ダイジェストと Angular 2 の変更の検出をブリッジし、両方のフレームワークにまたがる 1 つにまとめたダイジェスト サイクルを作成します。
一般的なアプリケーションのアップグレード プロセス
ここで、Angular 1 のプロジェクトの Angular 2 へのアップグレードの例をご紹介します。
- 既存のアプリケーションに Angular 2 と ng-upgrade ライブラリをインクルードします。
- 移行するコンポーネントを選択します。
- Angular 2 の構文に従って Angular 1 のディレクティブのテンプレートを編集します。
- ディレクティブのコントローラー/リンク関数を Angular 2 の構文/セマンティックに変換します。
- ng-upgrade を使用してディレクティブ(現在はコンポーネント)を Angular 1 のコンポーネントとしてエクスポートします(これは、Angular 1 のテンプレートから Angular 2 の新しいコンポーネントを呼び出す場合に必要です)。
- 移行するサービスを選択します。
- ほとんどの場合、必要な変更は最小限であるか、変更は不要です。
- Angular 2 でサービスを構成します。
- (オプション) Angular 1 のコードの他の部分でまだ使用されている場合、ng-upgrade を使用してサービスを Angular 1 に再びエクスポートします。
- 必要に応じて、ステップ 2 と 3 を繰り返します。
- サービス/コンポーネントの変換がすべて終わったら、最上位の Angular 1 ブートストラップをドロップし、Angular 2 のブートストラップに置換します。
個々の変更は個別に確認できるため、アプリケーションは動作を継続し、ユーザーは必要に応じてアップデートのリリースを続行できます。
我々は、コンポーネント以外のディレクティブを両方で使用できるようにすることは計画していません。コンポーネント以外のディレクティブのほとんどが新しいテンプレートの構文(つまり、ng-click vs (click) )で直接サポートされるため、Angular 2 では不要であると考えています。
質問と回答
Angular 2 では双方向バインディングがサポートされないと聞きました。交換するにはどうすればよいですか?
実際には、Angular 2 で双方向データ バインディングと ng-model がサポートされますが、構文がわずかに異なります。
Angular 2 の開発に着手したとき、Angular のダイジェスト サイクルに関する問題を解決したいと考えていました。そこで、変更の検出用に 1 方向のデータフローを作成することを選択しました。当初は、ng-model の双方向のフォーム データバインディングが Angular 1 にどのように適応しているのかが不明でしたが、Angular 2 のフォームを Angular 1 のフォームと同様にシンプルにする必要があることはわかっていました。
何度か試行錯誤を繰り返すうち、複数のダイジェストでの問題を解決し、Angular 1 の ng-model のパワーと簡潔さを維持することができるようになりました。
双方向データバインディングの新しい構文は次のようになりました:
[(property-name)]="expression"
。これによって、双方向で式が明示的にバインドされます。ほとんどのシナリオにおいて構文の変更が軽微なため、移行は容易であると考えています。
たとえば、Angular 1 での構文が
<input type="text" ng-model="model.name" />
の場合、
Angular 2 では
<input type="text" [(ng-model)]="model.name" />
に変換します。
Angular 2 で 使用できる言語はどれですか?
Angular 2 API では、現在の JavaScript(ES5)、次期バージョンの JavaScript(ES6 または ES2015)、TypeScript、および Dart でのコーディングが完全にサポートされます。
現在の JavaScript を引き続き使用していただいても何ら問題はありませんが、ES6 および TypeScript(これは ES6 のスーパーセットです)をお試しになることをお勧めします。これらを使用することで、生産性が大幅に向上します。
ES6 では、プロミスやモジュールといった共通ライブラリに、大幅に改善した構文や互換性のある標準機能を提供します。TypeScript では、コード ナビゲーション、IDE での自動リファクタリング、ドキュメンテーション、エラー検出などが大幅に向上します。
ES6 と TypeScript は ES5 のスーパーセットであるため、簡単に導入できます。これは、既存のすべてのコードが有効で、一度に少しずつ機能を追加できることを意味します。
コードベースの $watch はどのように扱えばいいですか?
簡単に言うと、$watch 式は宣言的なアノテーションに移行する必要があります。これに適さない場合は、オブザーバブル(リアクティブ プログラミング スタイル)を利用する必要があります。
Angular 2 で速度と予測可能性を向上させるには、宣言的にウォッチ式を指定します。式は HTML テンプレート内にあり、自動的にウォッチされる(何もしなくてもよい)か、ディレクティブの注釈で宣言的にリストする必要があります。
これによるもう 1 つのメリットは、Angular 2 アプリケーションを小規模なペイロードのために安全に圧縮/難読化できることです。
アプリケーション間のコミュニケーションのために、Angular 2 ではオブザーバブル(リアクティブ プログラミング スタイル)が提供されます。
移行の準備のために何をすべきですか?
ベストプラクティスに従い、
AngularJS Style Guide で説明されているように、Angular 1 のコンポーネントおよびサービスを使用してアプリケーションを開発してください。
当初のアップグレード計画では、新しいコンポーネント ルーターを使用することになっていませんでしたか?
ng-conf 2015 で発表したアップグレード計画は、ビュー全体を一度にアップグレードし、2 つのバージョンの Angular 間のコミュニケーションをコンポーネント ルーターで処理することを前提としていました。
元々インクリメンタルな移行をおすすめしていましたが、十分ではありませんでした。計画を再考し、上記のような計画に変更することになりました。
提供していただける情報はほかにもありますか?
はい。Angular 1 から Angular 2 へのアップグレード戦略設計のドキュメントに記載されています。
今後、次のような関連トピックの投稿を予定しています。
- Angular 1 の知識を Angular 2 をマッピングする。
- Angular 2 の詳細に関する FAQ。
- コード サンプルを使用した詳細な移行ガイド。
次回以降の投稿をお楽しみに!
Posted by
Eiji Kitamura - Developer Relations Team