※ 当ページには【広告/PR】を含む場合があります。
2020/06/02
2022/08/10
【Angular12対応】Angular UniversalでSSR入門〜Webサイト構築
Angular Universal を利用すると、動的にWebページをクライアント側に送り出すサーバーサイドレンダリング(SSR)だけでなく、静的htmlファイルへプリレンダリングできます。近年では、WebサイトのSEO対応の新しいスタンダードとしてSSRやSPAページのプリレンダリングなどが盛んに議論されるようになりました。すこし残念ですがまだあまり日本ではこういうトレンドはそんなに話題になってはいません。邦人のブログ運用から言うと、最初からWordpressのようなオールインワンの機能を持ったリッチなCMSサービスを利用してブログサイトを立ち上げるケースがほとんどであり、そもそもフルスクラッチでブログサイトを自作する人はかなり小数なのかも知れません。兎角、Angular Universalに限らず、JS系のフレームワークで、SSR/Prerenderingさせるテクニカルな内容を解説されているサイトは海外勢の技術ブログやドキュメントの盛り上がりとくらべると、日本での盛り上がりは少ないと言わざるを得ません。諸事情等ありますが、Angular Universalのネタは海外サイトの情報を仕入れて、自主勉強するより他はなさそうです。最近読んだこちらのブログにAngular Universalを理解する上での4つの注意点をまとめられていたのが印象的でしたので、著者なりの言葉で咀嚼しながら自分用の防備録としてもこの記事に残しておきたいと思います。Angular Universalを利用する際に、静的ページのプリレンダリング中に起こるおおよそのエラーは、サーバー(Nodejs)側とブラウザ(クライアント)側の仕組みの違いをあまり理解しないで、通常のAngularアプリケーションをビルドする感覚で起こっているといえます。逆に言うとクライアント側のコードとサーバー側のコードをより意識的に別けて書くことで、Angular Universalをより良く活用できるはずです。今回はSSR/プリレンダリングする時に吐き出されるエラーの傾向と、解決のためのおおまかな指針に関してまとめます。
【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集
その1 〜 Webブラウザのグローバルオブジェクトの扱い
ブラウザには標準的に備わっているグローバルオブジェクトもしくはブラウザオブジェクトという機能があります。代表格のwindow
やdocument
、location
といったものがグローバルオブジェクトとして各ブラウザに標準的に備わっています。通常のAngularのSPAでもDOMの仕組みを通じてhtmlを操作を可能としているのは、このグローバルオブジェクトの存在があるからと言えます。対して、サーバーサイドレンダリングやプリレンダリングはNodejs(サーバー側)であらかじめ処理をしてから、クライアント側へレンダリングした結果を送り出すことを行っています。Nodejsはブラウザと違って、DOMを操作するAPIは備わっていないので、いつものSPAをビルドする感覚でAngular Universalを使うと、以下ようなエラーに遭遇することがあります。Angular Universalがビルドしている主体はサーバーサイド側です。ブラウザー側の機能であるグローバルオブジェクトが何なのかNodejsにはコンパイル中に解決出来ないために起こるエラーのようです。このエラーはpolyfill.ts
にグローバル変数として与えても回避することは出来ません。今日日、サードパーティー製のnpmパッケージを利用しないプロジェクトなどは考えられないくらい何かしらのライブラリを活用されているかと思います。ブラウザ上で動作することを想定してwindow
やdocument
をグローバルオブジェクトとして使っているライブラリーをAngular Universalでビルドしたい場合は、dominoを使う必要があります。domino
は、既存のindex.html
を内部から読み取って、nodejsで利用可能な擬似DOMをサーバー側に渡してくれるライブラリです。プロジェクトにdomino
を導入するには、でパッケージインストールします。domino
をインストールしたら、server.ts
(もしくは環境によってはmain.server.ts
)に以下のようにコードを追加します。これで通常のSPAを出力するのと同じような感覚で、プリレンダリングが可能になります。
【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集
その2 〜 Angular Universalはできるだけ新しいバージョンを使う
ご存知のように、Angularのメジャーなバージョンアップが半年ごとにあり、頻繁にメソッドの用法・文法が変わっていきます。その都度メソッドの呼び出しに細かい見直しがあったのを気づかずに、ブラウザビルドは問題なく通ったけれど、Angular Universalではコンパイルでエラーが起こる...という不可解な状況になることもあります。特にサードパーティのnpmパッケージを利用するときに、そのメンバ関数からネイティブDOMにアクセスするようなライブラリを扱うときに注意が必要となります。ElementRef
クラスのnativeElement
を介したネイティブDOMの操作を一例を挙げてみます。とあるサードパーティ・ライブラリからネイティブDOMを操作するメソッドを実装したAwesomeRenderService
というクラスを作って、何かのコンポーネントで使うとします。としてDOMを扱うパターンが最近のangularでは多いかと思います。ただし、ElementRef
クラスのnativeElement
のメンバープロパティは@angular/core6.1以前のバーションで一部の機能が利用出来ないようになっています。Angular Universal
でサーバービルドしたときにコンパイルエラーが生じる可能性があります。もしそれより以前のangularでもDOM操作しなければならない場合には、後方互換の手段でRenderer2
を使わないといけなくなるかも知れません。DOM周りの実装すべてをRenderer2
ベースで書き換える必要が出てくると中々大変です。結局、苦労して後方バージョンのサポートするくらいであれば、プロジェクトのangular universal
のバージョンに合わせて、angular cli
をアップグレードすることを検討した方がベターです。
【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集
その3 〜 メモリーリークの確認
主にメモリーリークを確認するのは、通常のAngular SPAの開発のとき同様、SSRやプリレンダリングで開発するときも有効な手段です。JSヒープをモニタリングすることによって、Universalアプリケーションがメモリリークを起こしているかどうかを評価することが出来ます。Chrome DevToolsによるパフォーマンス測定の方法に関しては、以下のサイトで詳しく解説されているのでご参考いただくとして、本ブログでの詳細の説明は割愛します。フロントエンドのパフォーマンス改善とメモリリーク対策の方法Universalアプリケーションに限らず、Angularアプリケーションの主要なメモリリークの原因となるのは、実装したサービスクラスのサブスクリプションインスタンスをunsubscribe()
し忘れによるゾンビサービスの増殖です。Rxjsでunsubscribe
するテクニックはいくつかあるのですが、メモリリークが発生する場合にはまず自身のサービスクラスでサブスクリプションしているところを中心に原因を探してみましょう。
【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集
その4 〜 遅延ロードの利用
これは主にSSRを使う時の注意点です。サーバーサイドでレンダリングする際には、クライアント側からのリクエストを受け取ってからサーバー内部で様々な処理がされてから、レンダリング後のindex.htmlなどの結果がレスポンスとして返ってくる出力を受け取るという一連の流れでアプリケーションが動作しています。問題になるのは、クライアント側からサーバー側での処理に時間的な負荷のかかるリクエストを、非同期処理させてしまうことです。SSRする際にはサーバー側からのレスポンスを同期的に受け取る仕組みが必要ですし、リクエストの負荷が大きいとサーバー側の計算資源が枯渇してパンクしてしまいます。そもそもSSRの目的は、クライアント側(ブラウザ)でのリソースの読み込み時間や描画時間を軽減させ、より高速・快適にWebページをレンダリングする仕組みですので、サーバー側で長い処理時間が掛かってしまうと本末転倒です。そのような場合には遅延ロード(Lazy Loading)によって細かい粒度の処理リクエストに小分け・後出しすることで、サーバー側の負荷を軽減させるのがベターです。クライアント側からのクリックやスクロールなどのイベントでHttpClient
リクエストが発生するようにしてもいいのですが、IntersectionObserver
APIによる遅延ロードを検討すると見栄えの良いUI/UXに仕上がるかと思います。下の参考サイトなどに実装方法が解説してあるので、興味があればご参照ください。世界を平和に導く最強のlazy loadを構築した話
【効果的学習法レポート・2022年版】Angular(JSフレームワーク)をこれから学びたい人のためのオススメ書籍&教材特集
まとめ
最近ではangularに限ったことではないのですが、vuejsやReactでもUniversalアプリケーション対応が盛り上がってきているように感じます。サーバーサイドレンダリングやプリレンダリングはまだまだ発展途上な技術な上に、フロントエンドとバックエンドの知識が混じり合うようなこともあり、コードの実装が分かりにくいのが現状です。とはいえ、一度慣れてしまうとWebサイトの運営に多大なベネフィットを生み出す技術であると言えます。今後も気が向く限りUniversalアプリケーションの話題を紹介できたら良いと考えております。参照サイト
The biggest Angular Universal gotchas, and how to avoid them