Angular実行環境に関するプロジェクト「Angular Universal」

lacolaco氏:今日は「Angular Universal」という、Angularのサーバーサイドレンダリングを主に扱っているプロジェクトで、最近新しいおもしろいものが始まっているので、Node.jsコミュニティのみなさんにも共有したいなと思ってもってきました。コメントなど、お待ちしています。

自己紹介です。私、lacolacoといいます。Google Developers Expertという、認定エキスパートをやっています。また、Angularのコントリビューターなどをやっています。「Twitter」のアカウントは「laco2net」です。

今日のお品書きですが、LT(ライトニングトーク)ということで、前半は、Angular Universalについての簡単な振り出しをしたあとに、後半は「Clover」という新しいプロジェクトについて紹介をしたいと思います。

まずは、Angular Universalの現状について。何年か前にも、Node学園かNode学園祭かのどちらかで、Angular Universalの話をした覚えがありますが、Angular Universalは、Node.js上でAngularを実行することに関するいろいろなものが入ったプロジェクトの名前です。

アイソモーフィックやユニバーサルなどが一時期流行りましたが、名前はユニバーサルという、ブラウザーもサーバーも含めて、どこでもみたいな感じです。

主なトピックは、サーバーサイドレンダリングと、プリレンダリングです。静的サイト生成だったり、デプロイ前にレンダリングしたりという監視と、サーバー上でレンダリングして返すという監視の2つが大きい監視です。リンクは、ソースコードやドキュメントに書いてあります。

ざっくりとした概要ですが、たぶん世のサーバーサイドレンダリングとそんなに変わらないと思います。

まず、アプリケーション、SPAがブラウザー用にビルドすると、HTMLとJavaScript、CSSみたいな、デプロイすればブラウザーで動くものができあがります。

もう1つ、Node.jsで動かす用のNode.jsのエントリーポイントがあります。それがmain.server.tsみたいな感じであって、こちらはこちらでビルドします。

ビルドする時に、Browser Buildを一部取り込まないと、サーバーサイド上で同じコードを実行できないので、ソースコードの大半はブラウザーにも読み込まれて、サーバーにもどちらにも、ビルドに取り込まれます。

完全に生成し終わってて、ブラウザー上ではJavaScriptが動かないというパターンはそんなに多くないと思います。なので、ブラウザー上に出ていって動くようにビルドする必要があるし、サーバーサイドで動かすようにビルドする必要があって、2回ビルドが必要なんですね。

サーバーでやることは、Expressだったら基本的にはこんな感じです。リクエストが来たらパスがレンダーして返します。

Angular UniversalをExpressで使う時は、サクッと導入ができます。express-engineをインポートして、Bootstrapの設定をいじって、あとはレンダーすればいいです。

Angular UniversalをExpressでやるだけだったら、コマンド1個叩けば必要な設定などが全部生成されるので、3分ぐらいでSSR準備ができます。

これが今のAngular Universalの現状で、つまり順当に愚直にSSRするだけなら、特に困りごとはありません。そこで生まれたプロジェクト「Clover」がどういうものなのかを説明します。

実験的プロジェクト「Clover」

これは、まだ実験的なプロジェクトで、2021年4月に突然プルリクエストが飛んできて、明らかになったプロジェクトです。なので、まだぜんぜん安定していないし、機能も足りていないです。

最初は「何だこれ?」となっていたのですが、中身を見ていくと、コンセプトはAngular サーバーサイドレンダリングの中で「こうだったらいいな」という「ペインというほどのペインではないんだけど、もっとこうなったらいいのにね」というNice to haveみたいなものを検証していこうよという感じで、まずは「もっと簡単に導入できないか」です。

もう1つは、SPAをサーバーサイドレンダリングする時にありがちな、実行環境がNode.jsなので、windowやdocumentに依存してなにかを参照すると、Window is undefinedみたいな環境の違いに遭遇するということがないようにするです。

ほかにも、ビルド回数を1回にしたいよねということを、どうにかできないかと出てきたのがCloverで、何するかというと、jsdom内でAngular実行するのを今試しているようです。

jsdomは何かというと、Node.js上にDOMのAPIを実装した、擬似的なDOM APIみたいなものです。Jestやテスト用フレームワークの中で使われていたり、jsdom単体でツールの一部として使われていたりして、要するにDOMの中にwindowやdocumentなど、APIの実装があります。そこにHTMLを読み込ませて、そのHTMLの中のscriptタグ経由で、Angular実行します。ブラウザーでやることとまったく同じことを、Node.js上のDOM APIの中でやってみると、アプリケーションは自分がNode.js上で動いていることを知らないまま終わります。

どうなるかというと、ソースをビルドするとブラウザー用のビルドができて、HTMLとJavaScript、CSS、基本的なパーツができあがります。これをプリレンダーする時に、readFile('index.html')にして、HTMLファイルをjsdomに食わせて、その中のscriptタグやstyleタグなどを全部普通にブラウザーと同じように動いて、できあがったあとのDOMをシリアライズして、Stringと文字列を吐き出して終わるというアプローチです。これが、jsdomのScript Executionという機能を使って行われています。

同じことをやっているのがほかにあるのかちょっと気になっているので、もしあったらコメントやTwitterのハッシュタグで教えてほしいです。けっこう珍しいなと思ったので、知見がありそうなNode.jsコミュニティにぜひいろいろと教えてほしいです。

jsdomの中でスクリプトを実行するのはいろいろと制約があって、Node.jsが内部でもっているVMというモジュールを使っているんですが、そこで一定の機能に制限された、サンドボックス化された環境でJavaScriptを実行します。何ができるかというと、windowやdocumentやXHRはあるのですが、process、module、fs、osなどには触れなくなっています。

なので、アプリケーションからすると環境は完全にブラウザーになっています。

ユースケースは、まずはSSG(Static Site Generation)の対応です。デプロイ前に全部ビルドしきっちゃうというユースケースと、もう1個はプリレンダリング。SPAをデプロイ前に「ガワ」だけレンダリングしておいて、コンテンツ部分は空けといてみたいなパターンに使います。

ありがとうございました。