こんにちは、編集長の白石です。ぼくは、自社で運営と開発をしているTechFeedというエンジニア向けニュースアプリにAngular(とIonic)を採用していることもあり、Angularの行方には強く関心を抱いています。が、一方で、日々の忙しさにかまけてなかなかアップデートを追えていなかったりもします。
しかし、先月(2017年3月)にAngular 4.0がリリースされ、ng-conf(アメリカで開催されたAngularのカンファレンス)も終わった今は、Angularの最新情報を得るのに最適な時期!
そこで、ng-japanの代表であり、ng-confにも参加されていたlacoさんに、Angularの最新トピックとテクニックについてお聞きしてきました。初心者向けの記事ではありませんが、大変濃くて面白い記事になりました!Angular使いの皆さま、どうぞお楽しみください。
しかしあまりにロングなインタビューだったので、さすがに記事分けました。今回の記事は後編です。
⇒ 前編はこちら。
Angularのモジュール…分割、共有、遅延
白石 では次に、Angularのモジュールシステムについてもお聞きしたいです。そもそもAngularのモジュールとはどういうものですか?
laco はい、Angularのモジュールは、NgModuleといわれるもので、ECMAScriptのモジュールとは異なります。コンポーネントやサービスなど、Angularの構成要素をひとまとめにしたものです。AngularJS (1系) のモジュールと、実は大差ありません。
Angularのアプリケーションは、モジュール単位で分割されます。モジュールには二種類あります。
ひとつはアプリケーションのルートモジュール。アプリケーションを起動する際に必ず必要となるモジュールです。もう一つはフィーチャーモジュールといって、他のモジュールにインポートされて利用されることを前提としたモジュールです。サードパーティのライブラリや、アプリケーションを複数モジュールに分割したときなどに、フィーチャーモジュールを作ることになるでしょう。
白石 モジュールについてのベストプラクティスとかがあったら教えてください。
laco ライブラリ作者としては、Angular関連のライブラリは、自身をモジュールとして提供するのがベストプラクティスになっています。
アプリケーションをモジュールに分割する場合は、任意の単位でモジュールに分けて良いのですが、フィーチャーモジュール同士が依存しあわないような設計にするのがベストです。 そのために、モジュール間で共通する処理をまとめた共通(シェアード)モジュールを作るようにして、依存関係を整理するのも重要です。
白石 モジュールを分割してルートモジュールを小さくすることで、起動時間も速められるんですよね。
laco そうです。そうした場合は、起動に不要なモジュールは遅延ロードする必要があります。最初からすべてを読み込まないようにすることで、起動を速めるわけですから。
白石 具体的にモジュールの遅延ロードってどうやるんですか?
laco 遅延ロードはルーティングをベースとしているので、モジュール分割もルーティングの設計と不可分になります。具体的な方法としては、ルーターの設定でloadChildren
に別モジュールのパスを指定することで、自動的にAjaxを使ってモジュールを読み込んでくれます。また、画面遷移が発生してからモジュールを読み込むとユーザーを待たせてしまうので、遅延ロードするモジュールを先読みしておきたいという場合は、preload
という仕組みも使えます。
ルーターはほぼ完成形、ではなぜIonicは使わない?
白石 では、ルーターそのものについても教えていただきたいんですが、Angularのルーターは本体のバージョンを追い越して3が出てしまい、それと歩調を合わせるべくAngular本体のバージョンが4になったり…といろいろバタバタしていた印象ですが、現在はどういう状況なんでしょうか?
laco ルーターに限らず、フォームやチェンジディテクション周りも開発したVictor Savkinという天才がいまして、彼によればもうルーターはほぼ完成の域に達しているそうです。十分安定しており、今後大きな変更が入ることはないと。
白石 それは心強いですね!機能も、APIの安定性も十分ということでしょうか。ただ一つ、ちょっと気になってることがありまして。
ぼくらはIonicを使ってアプリ開発を行っているのですが、IonicがなぜかAngularのルーターを使わずに、独自の仕組みになってしまっているんですよね。ルーターがそれほど高機能・高品質なのであれば、なぜIonicがそれを使わなかったのかな、と。まあ、Ionicの話なんで「知らない」ってお答えでも全然構わないのですが(笑)。
laco それは、ルーターがDOMを置き換えることを前提に設計されているからだと思います。つまり「トランジション」(遷移)です。 一方IonicはモバイルアプリのようなUXを目指しているので、UIを次々に「スタック」していく。
そういう「トランジション」と「スタック」という、根本的な設計思想の違いがあるので、Ionicもルーターを活用できなかったのではないでしょうか。
白石 なるほど。では、そういうUIがスタックしていくようなタイプのアプリを作りたい場合は、Angularのルーターの対象範囲から外れてしまっているのかもしれませんね。
Ionicだと、代わりDeepLinker
って仕組みが提供されてるんですが、これもあまりイケてるとは言えないので悩ましいんですよね…。
パフォーマンスについてはもう話にも上がらないレベル
白石 AngularJSでは、パフォーマンスについて欠点を指摘されることも多かったように思いますが、現在のAngularではいかがでしょうか?パフォーマンスはかなり向上した、というのが定説ですが。
laco Angular開発者の話だと、「Angularはもう充分に速いので、パフォーマンスについてはもうAngularに任せてくれ」というレベルに達しているそうです。ng-confでも、もうパフォーマンスについては「話にも上がらない」って状態でしたね。
白石 「話にも上がらないレベル」ってすごいですね。では、パフォーマンスについては開発者は何か気をつけておくとか、対策しておくことって必要ないんでしょうか。
例えば、コンポーネントはとりあえずすべてChangeDetectionStrategy.OnPush
を指定する…などのプラクティス(※)はいかがでしょう?
laco うーん、OnPush
にするのは望ましいっちゃ望ましいのですが、全部のコンポーネントをそうするのは骨が折れますよね。
白石 はい、非同期処理終わったらいちいちChangeDetector.markForCheck()
するのが面倒で面倒で(笑)。
laco 一概にOnPush
にするというのも考えものかもしれませんね。デフォルトの変更検知アルゴリズムを使って、あとは「Angularを信じろ」(笑) 。というのもありじゃないかなあとは思います。
白石 パフォーマンスについては、ほかに知っておいたほうがいいトピックとかありますか?
laco フレームワーク自体は速いとしても、すべてがAngularで完結するわけではありませんから、Angular以外の部分には注意が必要かもしれません。例えばHTTPによる通信だとか、Zone.jsだとかですね。
白石 ああ、Zone.jsとか、結構無茶してますよね(※)。いろいろ気をつけるべきところありそうな。
laco Angularに管理してほしくない非同期処理は、NgZone.runOutsideAngular()
というメソッドで、Zone.jsの管理外で実行できるというのは知っておいていいと思います。例えばスクロールやスワイプなど、小さくて大量に発生するUIイベントとかは、このメソッドを使えば高速化が期待できます。
※デフォルトだと、変更検知処理(ChangeDetection)はコンポーネントツリーをすべて走査するが、OnPush
を指定したコンポーネント(とそのサブツリー)は、ChangeDetectionの対象から外すことができる。ただし、ChangeDetectionは非常に高速なので、通常はデフォルトでよいとされている(参考)。
※「Zone.jsとか、結構無茶してますよね」…Zone.jsは、setTimeout()や各種のDOMイベントなどをすべて乗っ取って、複数の非同期処理をまたいだ実行コンテキストを構築する。
サーバサイドレンダリング、どんな機能?
白石 では、Angular 4.0の目玉であるサーバサイドレンダリング(SSR)についてはいかがですか?
laco 結局今はNode.js限定になりました。昔コミュニティ主導で作っていた時は違っていたのですが。
白石 昔はASP.NET向けの実装とかもあったみたいですよね。
laco はい、でも今は組み込まれていません。以前のUniversalは、風呂敷を広げすぎて追いついてなかったんですよね。今はその反省を活かして、まずは最小限の機能を提供し、拡張についてはコミュニティに任せるという役割分担を目指しています。
SSRの使い方は簡単で、@angular/platform-browser
の代わりに@angular/platform-server
を使用して、bootstrapModuleFactory
の代わりにrenderModuleFactory()
を呼び出すだけです。
そうすると、レンダリング結果を文字列で受け取ることができるので、それをクライアントに返せばいい。
白石 なるほど。つまり基本的な動作としては、サーバーサイドでAngularアプリケーションを完全に立ち上げるということなのでしょうか?
laco はい、そうです。Angularアプリケーション全体がサーバサイドで実行されます。すごいのは、ルーターやHttpもそのままで動くよう設計されている点です。ルーターが動くということは、リクエストされたURLに応じて自動的にレンダリング結果が変わるということです。Httpを使っている部分は、Node.jsのhttpモジュールを使用してリクエストが行われます。
白石 クライアントサイドで実行したときと同様に、コンポーネントを動作させた結果がHTMLソースとしてレスポンスされるということですね。 その後クライアント上で立ち上がったAngularが、DOMを入れ替えるという動きになるんですか?
laco その通りです。そのDOMの入れ替えも、ユーザーには気づかれないくらいスムーズに行われます。
白石 なるほど。ちなみに、昔話題になったpreboot
とかは使えるのでしょうか?
laco いえ、組み込まれていませんね。これも、まずはシンプルな実装を提供するという目標に沿ったものです。もちろん、使おうと思えば自分で組み込むことはできます。
白石 ちなみにパフォーマンスについてはいかがですか?以前、「React vs Angular」というお題で対談して頂いた時、ReactのSSRは結構遅くて大変…なんてお話もありましたけども。
laco AngularのSSRは、文字列で書き出す以外の余分な処理を極力しないようにしているので、Reactのものより速い可能性はありますね。余計なDOMもほとんど出力されません。
Progressive Web AppsとAngular、そしてAMP
白石 ほかになにかありますか?個人的に気になっていたのは、Web Workersも利用できるようにするとか。
laco platform-webworker
はなくなる予定なんですよね。
白石 えっ!気になってたのに…
laco AOTで十分高速なので、Workerとか使うとかえって複雑になってしまうから、もういらないんじゃないかって話になりつつあります。
その代わり、ng-confでもServiceWorkerの話は結構ありましたね。Progressive Web Appsの話は、Angular 4.0リリース後盛り上がってきています。
白石 おお、PWAですか。どんなトピックが取り上げられていましたか?
laco まず、mobile.angular.ioというサイトがありまして、ここではAngularをモバイルで動かすためのツールキットを作っています。モバイル対応に必要なタスクをすべてAngular CLIで実現することを目指しているプロジェクトです。
Progressive Web Appsの対応って、意外と面倒じゃないですか。ServiceWorkerでオフライン対応するのも面倒だし、Web App Manifestも作らなきゃいけないし。そういうタスクを、「Automatic PWA」と言って、手間をかけずに実現できるようにしようとしています。実は今のAngular CLIにも、隠された機能としてPWA対応があるんですけどね。
白石 PWAと言えば、AMPと組み合わせて「PWAMP」なんて言ってたりもしましたね。ぼく、具体的にどうするのか知らないのですが、どういうことですか?
laco 「PWAMP」って言葉はもう使われてないみたいでした(笑)。AMPには、ServiceWorkerを読み込ませる機能があるのを利用して、ServiceWorkerを裏で読み込んで起動しておくんです。そうすれば、ServiceWorkerのオフラインキャッシュなども処理を実行させておける。
さらに、AMPページに「もっと読む」というボタンなどを用意しておけば、すでにキャッシュ済みのリソースを使ってPWAが立ち上がるので高速、というわけです。
TypeScriptのバージョンアップが与えた影響は?
白石 Angular 4.0からは、TypeScriptのバージョンも2.2に上がりましたね。TypeScriptはすごい勢いでアップデートされていますが、TSのアップデートのおかげでAngularが変わった部分などありますか?
laco TypeScriptにはLanguage Serviceという仕組みがあるんですが、それを利用してAngularがエディタにテンプレートのシンタックスエラーを報告するなんてことができます。Visual Studio Codeでは、拡張機能として「Angular Language Service」というのが提供されていて、Angularのテンプレートの構文チェックなどを行えます。WebStormには、標準機能としてすでに組み込まれています。
TypeScript 2.2ベースの現在の実装だと、まだ無理をして実装している部分があるのですが、2.3になったらそうした部分も書き直される予定です。
AngularJSからのアップグレード
白石 ほかに、ng-confで話題だったトピックとかありますか?
laco 目立ったのはAngularJS (1.x) からのアップグレードに関するトピックですね。いまだに1系を使い始める人がまだまだ多いみたいなんです。 Angularチームはドキュメントの参照数をカウントしているのですが、AngularよりもAngularJSのほうがかなり参照数が多い。130万と80万という比率だそうです。
白石 以前に比べてアップグレードはしやすくなっているんですか?
laco はい、ドキュメントが少ないのが残念ではあるのですが、UpgradeModuleという仕組みが利用できます。ただ、UpgradeModuleはAngularJSとAngularを同時に使うので、アプリケーションのサイズが肥大化してしまうんですね。
ただそれを、遅延ロードで解決することもできます。AngularJSのページとAngularのページを分けられれば、Angularの部分は遅延ロードが行えるだけでなく、AOTすらもできるようになっているんです。
白石 なるほど。しかし、今から1を使い始める人も結構いるっていうのもびっくりです。TypeScriptが壁になっているということは考えられませんか?
laco それは確かにあるかもしれませんね。
白石 じゃあ、AngularアプリをJSで書こうっていう話はなかったんですか?一応、JSで書くためのドキュメントもあるみたいですが…。
laco そういうセッションは全然ありませんでしたね(笑) 。あまり推奨もしていないんじゃないかなあ。
その代わりに例えば、「まずはWebサイトをやめてSPAにしよう」っていうセッションとかはありましたよ。AngularJSの時代は、通常のWebサイトの一部だけAngularJSを使う…という使い方もされていましたから。
白石 なるほど、そんなライトな感じのセッションもあったんですね。今日はアドバンストなトピックだらけだったので、最後にちょっとほっとする話題が聞けてよかった(笑)。
では、今日は長時間に渡ってたくさんのことを教えていただき、ありがとうございました!
⇒ 前編はこちら