SPA, SSR, プリレンダリング(SSG), React, Vue.js, Next.js, Nuxt.js。
正直、いまのWebの技術はいろんなことが多すぎて、すべてを理解できません。似てることも多いし。
こういうときは、しっかり基本に立ち返ります。そうすれば振り回されないし学習が速くなる。
Next.jsとNuxt.jsのちがいであれが良い、これが良いという情報に疲れてきました。
(好みで言ってない? ポジショントークになってない?)
(どっちもいいと割り切ったほうがいいんじゃないか?)
それよりも、『Webサーバーってどうなってるの?』『ブラウザって何してるの?』を知って、どうやってWebページは表示されるのか、大まかなイメージをもっておくほうがいいと思って、自分の復習用に書いています。
(出てきては消えていく技術情報ばかり追いかけたら堂々巡りになる。)
そのあとに、SPA, SSR, SSGについても触れたいと思います。
(あくまでざっくりとしたイメージです。)
『ブラウザは何をしているのか?』が大事になった理由
2015年くらいまで、『ブラウザは何をしているのか?』を知らなくても、それなりにWeb屋としてやっていけていました。
(知らなくてもよかったとは言ってないです。念の為。)
HTML, CSS, JavaScriptを書いて、サーバーにアップしたらページは表示されるし、DBアクセスなどの動的処理が必要なら、フレームワークを使ってPHP, Ruby, Javaでプログラムを書けばよかったので。
早いうちからAnglar(AnglarJS)やExpress.jsを触っていた人には信じられないと思うが、現実にはそういうところだらけだった。
(仕事では。今でも多い。)
『SPA, SSRは知られてるけど実際に使ってるのは一部』がボクの感覚。
どちらかといえば、それらのコーディング技術を磨くほうが必要とされていました。
それはいまでも変わりませんが、ボクにはまだ、SPA, SSR, SSG...などのちがいが自信をもってイメージできません。
これらの技術の進歩・改善は『ブラウザは何をしているのか?』のところが関係しているからです。
サーバーサイドの処理の流れ
まずはWebサーバーの処理から説明します。
サイトのパフォーマンス向上は『サーバーの仕事』でした。PHPなどのプログラムやDBアクセスの速度の改善がパフォーマンスの改善だったからです。
ボクは、SPA, SSR, SSGなどの技術は、サーバーサイドだけでは限界が来たから出てきたんじゃないかと思います。
(SSRはまたちょっとちがう。)
『ブラウザの仕事』も含めた全体的なアプローチの必要性から来たんじゃないかと。
ということでサーバーの処理の流れを見てみましょう。
静的ページ
静的ページは初期のWebサーバーの構成です。
HTML, CSS, JavaScript, 画像などのファイルを置いてあるだけで、リクエストを受けたら対象のファイルを返すだけのシンプルな処理です。
シンプルなだけに一番処理が速く、ページ遷移は新しいリクエストになるので、そのたびに図の処理が走ります。
ただこの構成では当たり前のことができません。ログイン機能、コメントの書き込み機能など、昔からあることでさえもできません。
それができるのが動的ページです。
動的ページ
いまでも一番使われている息の長いWebサーバーの構成です。
サーバー上にHTMLファイルを置かず、プログラムでHTMLを作成してブラウザに返します。
CSS, JavaScript, 画像ファイルなどは、HTMLを構成するパーツとしてサーバーに必要。
(CDNで外部から取ることもある。)
その分だけ処理に時間がかかりますが、できることは膨大でサイトの枠をこえてアプリケーション化されました。
(Webアプリ)
ブラウザはアプリケーションのUI(ユーザー・インタフェース)の役割だけを担っているのが特長です。
だからHTML, CSS, JavaScriptは、『フロント・サイド』と言われる。UIを担当するので。
対して、『プログラムの処理』の部分を『バック・エンド』という。
非同期リクエストで劇的に進化
Ajaxなどリクエストの非同期処理もできるようになり、ページを遷移しないでサイトの一部のコンテンツだけを更新できるようになりました。
(Googleマップが出たときは衝撃だった。)
また『プログラムの処理』の部分が進化し、WordPressなどのCMSやLaravelなどのフレームワークが有名です。
ただ、複雑な処理をできるだけにサイトのパフォーマンスはあまり良くありません。
WebサーバーやDBのキャッシュなど高速化対策がなされますが、『プログラムの処理』が多く発生するので、Webサーバーがあるマシンのリソースの消費量が大きいのが現状です。
WordPressを運用・管理している人は実感できるはず。
サーバー・マシンのリソース消費の大半が、DBやPHPなどのプログラムだからです。
(Webサーバー自体のリソース消費は大したことない。リクエスト受信とプログラム結果を返すだけなので。)
動的プログラムは独立している
上の図ではWebサーバーの一部として表現していますが、動的プログラムはアプリケーションサーバーとして独立させるのがふつうです。
Webサーバーとアプリケーションサーバー間のことをCGI( Common Gateway Interface )といいます。
プログラム言語・Perlが全盛の頃は、その動的プログラムのことを『CGIプログラム』とも言いました。
(最近はあまり聞かない。)
Nginx(Webサーバー)・PHPの構成ではアプリケーションサーバーにphp-fpm(FastCGI Process Manager)を使いますが、これもCGIです。
(名前にCGIが入っている。)
あとで出てくるSSRでは、アプリケーションサーバーの役割が重要になってきます。
プロキシサーバーとは?
プロキシサーバー(Proxy Server)は代理サーバーです。会社などある程度の規模をもつ組織のネットワークで使われます。
社内から外のネットワークにつなぐとき、社内ネットワークを代表して外のネットワークとつながり、外から見えるネットワークはプロキシサーバーだけなので、社内ネットワークはブラックボックス化されます。
セキュリティ構築でよく出てくるサーバーですね?
プロキシサーバーは、メール送受信のプールなどのキャッシュ機能や、社内でエロサイトが見れなくするなどのフィルタリング機能をもっています。
(不正なネットワーク接続を検知・遮断することもできる。)
また、メールは社内のメールサーバ、外部からの社内システムへの接続には専用のサーバーなど、受けつけたリクエストを振り分けるロードバランサーの機能もあります。
リバースプロキシサーバーとは?
プロキシサーバーはユーザーのネットワークに設置します。
(Webではサイトを見る側。)
それとは逆に、サービスを提供するサーバー側に設置するのがリバースプロキシサーバー(Reverse Proxy Server)です。
(WebではWebサーバーを設置する側。)
リバースプロキシも同じような機能をもち、複数のWebサーバーやDBサーバーを設置してロードバランサーでリクエストを振り分ける、システムの負荷分散を構築することもできます。
Nginxにかぎらず、『Webサーバー』と呼ばれる製品はリバースプロキシの機能をもっています。
動的サイトのWebサーバーは、仕事をアプリサーバーなどへ丸投げして結果だけを返すので、
一般的にWebサーバーと呼ばれるもの = リバースプロキシ
と考えていいです。
大規模なWebシステムでは、ひとつのWebサーバーをリバースプロキシとして使い、ほかに複数のWebサーバーを起動して、リバースプロキシから各Webサーバーへリクエストを振り分けて処理を分散させる。
各WebサーバーにそれぞれDBサーバーを設置することもある。
(そのときはDB間の同期が必要。)
ブラウザは何をしているのか?
ここで一旦、サーバーの処理はサヨナラしましょう。本命のブラウザは何をしているのか? の話をします。
これがSPA, SSR, SSGに関係してきます。
細かいフェーズに分かれてますが、処理はシンプルです。
HTMLを読み込んで必要なファイルを取得し(CSS, JS, 画像など)、HTMLとCSSの内容をメモリに展開して、そのメモリを使ってJavaScriptを実行します。
そのあとHTMLとCSSのメモリを結合、コンテンツの位置・サイズを計算して最後にブラウザに表示します。
細かくフェーズに分かれているのは、一気に作業をせず少しずつ描画することでユーザーが待たされるのを防ぐため。
ブラウザの並列処理の弱点
並列処理にも弱点はあります。前の処理に時間がかかってしまうと、次の同じフェーズの処理は待たされます。
とくに、LoadingとScriptingは時間がかかりやすいフェーズです。
Loadingはファイルのダウンロード、HTML・CSSの読み込み、Scriptingはプログラム・コードの読み込みがあり、ディスクのI/Oが発生します。
(ディスクI/Oは処理に時間がかかる。)
それに比べれば、メモリ上で作業するRendering, Paintingは大したことありません。
CSS, JavaScript, 画像ファイルは圧縮しましょう。
余計なHTMLタグを書くのはやめましょう。
CDNを使いましょう。
CSS, JSのローディングを遅らせましょう。
などなど基本的な施策がありますが、これらはRenderingを早く始めるためのLoading, Scriptingの効率化です。
レンダリングブロックとは?
サイト表示のツール、PageSpeed Insightsで出てくるレンダリングブロックの犯人もLoadingとScriptingです。
レンダリングブロックの対応はCSSやJavaScript、画像ファイルに集中してますよね?
Renderingに使ってないのに時間だけ食ってしまうものを排除すれば、レンダリングブロックは起きずブラウザの表示時間を短縮できます。
並列処理はもっと複雑
ここでは説明しませんが、Loading, Scriptingの並列処理はもっと複雑です。
Loading | |
---|---|
download | css, js, 画像などのファイルのダウンロード。 |
parse | HTML, CSSを上から読んで構文解析すること。 読み終わったものを順次メモリ(DOM, CSSOM)に展開していく。 |
パースの途中でJavaScript(<script>)が見つかったらパースを停止してスクリプトコードを実行するとか。
(スクリプトが終わったらパースの続きをする。)
async属性を使ってスクリプトを非同期に読み込むとか、preload属性を使ってcss, jsの読み込みの優先度を上げるとか。
今回はブラウザの仕事のイメージをもつことを目的にしているので詳細は割愛させていただきます。
解決のヒントになるワードはあるので、それを元に調べてみてください。
最近お盛んな処理の流れ
ここまでで、Webの大まかな処理の流れは分かったと思います。ブラウザのページを表示するのに使う時間は
サーバープログラムがHTMLを作成する時間
+
ブラウザがレンダリング完了するまでの時間
(css, js, 画像のダウンロード)
(HTML, CSSのパース)
(スクリプトの実行時間)
(HTMLとCSSを結合して描画準備。)
がほとんどです。
(ブラウザ・サーバー間通信の転送時間とか、Webサーバーの処理時間とかあるが、大したことないし改善する余地はあまりない。)
いまでもサーバープログラムの改善は進んでいますが、モダン技術と言われるものは、ブラウザがレンダリングするところまで考えられているのが特長です。
SPA( Single Page Application )
で生まれたのがSPAです。シングルページと言いながらサイトのページ遷移はあります。urlだって変わります。
でも、ページ遷移のためのWebサーバーへのリクエストは1回だけ。ブラウザ側のJavaScriptで仮想ページ遷移(ルーティング)しています。
ページ遷移、各ページのコンテンツ切り替えの仕事をサーバーからブラウザへもってくることでサイトの高速化を図りました。
React, Vue.js, Anglar...。フロントサイドのMVCモデル・フレームワークの有名どころはSPAを作るためのフレームワークです。
(正確にはReactはライブラリ。)
SPAが出てきたからフロントサイドのMVCモデル・フレームワークが生まれたとすら感じます。
SPAが出てきたとき、Webのフロントでこんなことまでするんだと思った。
とうとうフロントサイドでもMVCモデル・フレームワークを使い出したか?という感覚。
(ボクはもともとサーバーサイド・エンジニア。)
(JavaやC++ではMVCモデル・フレームワークはすでに当たり前だった。)
変更点だけ変えればいい省エネ・スタイル
SPAは、Webサーバーからもらった唯一のHTMLファイル(index.html)をひな形にして、ページの一部だけをJavaScriptのDOM操作で変更し複数ページを表現します。
たとえば、ヘッダー・フッター・メニューは全ページ共通で使い、ページ固有のコンテンツだけを変更するとか。
また、ReactやVue.jsのSPAは仮想DOMを採用しています。
HTMLの構文をメモリ上に展開したDOMを直接編集せず、仮想DOMを用意して管理し、変更のあった差分だけを本物のDOMに反映させます。
ページが変わるたびにページ全体のDOMを作り直さないので時間の短縮になります。
使えるものはトコトン使い回す省エネ・スタイルです。
見た目がネイティブ・アプリ
SPAの特長はなんといっても『Webっぽくない』ところです。ページの遷移をせずにコンテンツを変更していくので、ネイティブ・アプリケーションのように見えます。
Webの技術はOSに依存しないので、ネイティブのスマホアプリの代わりにSPAで作ることもあります。
レスポンシブ・ウェブ・デザインで作れば、OSどころか、PC・タブレット・スマホなどのデバイスでも、1つのアプリケーション・コードで同じ機能を提供できる。
SPAのデメリット
SPAにも弱点があります。『初期表示時の遅さ』です。
最初にサイトの1ページ目を表示するとき、ルーティングなど結構なサイズのJavaScriptプログラムをダウンロード・実行するので時間がかかります。
本来のブラウザの仕事、Loading, Scriptingに時間がかかる。
ただし、2回目以降は速い。
ページごとにOGP設定ができない
もともと1つのページのコンテンツをDOM操作で変更してるだけなので当たり前ですが、ページごとにOGP設定ができません。
OGPは、SNSサービスでURLリンクを貼り付けるとスニペットで表示するためのHTML定義。
スニペットは、ページ画像・タイトル・ディスクリプションが表示される。
JavaScriptのDOM操作で<meta>を変更してもいいですが、肝心のTwitterやFacebookなどのSNSサービスが、今でもその動的変更を読み取れないみたいです。
(2020/3/31 現在でも『できる』という情報は見当たらない。)
そこでSSRを使いましょう、プリレンダリング(SSG)を使いましょう、という話になります。
Googleなどの検索エンジンのクローラーもJavaScriptの動的変更を認識できないと言われてましたが、今はできます。
クローラーが見るときはページの内容がスッカラカンだからSEOの点で不利だったという話ですね?
SPAの使いどころ
SPAは『アプリケーション』として価値があると思います。
ブログなど、サイトのページ数が頻繁に変わるものはSPAにするメリットを感じません。
正直、OGP設定に関しても気にしてないです。それをやろうとする時点でSPAの選択肢から外すことを考えるので。
アプリケーションをSNSで認知してもらうならOGP設定は1つでいいかなと思うし。
(OGP設定をページごとに設定することは否定しない。)
あくまでケース・バイ・ケースの話です。
SSR( Server Side Rendering )
となったのがSSRです。
SSRはSPAの改良版で、ページの遷移とコンテンツ切り替えはブラウザの仕事のままです。
でもここからが変わっていて、ページ固有の非同期リクエストのレスポンスで、レンダリング済みのHTMLを渡します。
ブラウザの大半の仕事をサーバーへ投げました。
従来の ブラウザ の仕事 | SPA | SSR |
---|---|---|
Loading | ブラウザ | サーバー |
Scripting | ブラウザ | サーバー |
Rendering | ブラウザ | サーバー |
Painting | ブラウザ | ブラウザ |
ブラウザの仕事は最後の描画だけ。
サーバーマシンにNode.jsが必要
サーバーでブラウザの仕事を引き受けるには、ブラウザのLoading, Scripting, Renderingを実行するアプリケーションサーバーが必要です。
SSRのために作られたフレームワークには、ブラウザの仕事を一手に引き受けるアプリケーションサーバーも用意しているので、フレームワークを使って開発しましょう。
Next.js | React.jsベースのフレームワーク。 コマンド: next start でアプリサーバーを起動。 |
Nuxt.js | Vue.jsベースのフレームワーク。 コマンド: nuxt start でアプリサーバーを起動。 Next.jsに感化されて作られたので構成は似ている。 |
ただし、これらはNode.js上で実行するので、サーバー環境にNode.jsが必要です。
next, nuxtコマンドが起動するアプリケーションサーバーの中身はNode.js上で動くWebサーバー。
Nginx, ApacheなどのWebサーバーをリバースプロキシとして使い、その裏で動くWebサーバーとして使う。
開発時には、テスト・確認用のローカルWebサーバーとしても使われる。
レンダリング結果の中身はHTMLです。
ただし特殊なHTMLで、CSSファイルの定義やコンテンツのサイズ・位置が計算済みのCSS定義が追記されています。
また、JavaScriptで変更したDOM操作の結果も反映されてます。
HTML + CSS計算結果 + JS結果 = レンダリング済みHTML
OGP設定もOK
サーバーで完成したHTMLを作るので、それまでにページごとのOGPを設定すれば静的ページと同じです。
ブラウザは完成したHTMLしか知らないので動的に作る必要がありません。もちろん、検索エンジンのクローラーやSNSサービスが見落とすこともありません。
SSRのデメリット
差分とはいえブラウザの仕事の大半を引き受けるのだから、サーバー負荷が高いです。
また、アプリケーションサーバー(Node.js上のWebサーバー)をNginxなどのリバースプロキシと連携するなどの作業が必要ですし、運用を考えた施策が必要でしょう。
(アプリケーションサーバーが落ちたときのリカバリ機能を実装するなど。)
処理に耐えられるサーバーマシンのスペックも必要です。
たしかにこれでSPAの弱点を克服できますが、『初期表示の遅さ』のためだけにこれだけのことをする必要があるのか? 疑問です。
SSRの使いどころ
大規模サイトで予算が十分にあるなら導入を考えますが、小中規模では構築した環境をフル活用できないように思います。
初期表示の遅さに対応するためにここまでするの? もそのひとつ。
そもそもSPAの初期表示の遅さを克服する必要があるのか?
導入以前の細かい検討が必要です。
プリレンダリング(SSG)
になったのがプリレンダリングです。
プリレンダリングを直訳すると意味不明ですが(事前レンダリング?)、SSRの環境からツールを使って静的ページを作りサーバーに置く、Webサーバーの初期の構成です。
プリレンダリングはSPA, SSRとちがい、JavaScriptを使ったブラウザでのページ遷移をしません。
ページ遷移は従来のWebサーバーの仕事です。サーバー上には各ページのhtmlファイルがあるのでそれを使います。
しかもその中身はレンダリング済みHTMLなので、ブラウザの仕事が削減されます。
SSG(Static Site Generator)って何?
SSGは静的ページを作るツールのことです。名前のまんま。
(静的サイト生成器)
Reactのライブラリ(Next.js)、Vue.js(Nuxt.js)などのフレームワークにはSSG機能があります。
これらを使えばかんたんに静的ページを作れます。あとはWebサーバーにHTML, CSS, JS, 画像ファイルなどをデプロイするだけ。
なんか先祖返りしちゃってますね?
Gatsby | Reactベースのプリレンダリングでサイトを作るライブラリ。 (プリレンダリングに特化したフレームワーク) 『プリレンダリングで作る』と決めたときは最適。 SPAやSRRとのハイブリッド、切り替えをしたいのならNext.js, Nuxt.jsのほうがいい。 |
Next.js | Reactのライブラリを集めたSSRサイト作成フレームワーク。 SSGを実行してhtmlファイルを作成するコマンドが用意されている。 |
Nuxt.js | Vue.jsの基本的なパッケージを集めたSSRサイト作成フレームワーク。 SPAのサイトも作れる。 SSGを実行してhtmlファイルを作成するコマンドが用意されている。 |
SSGではSSRの環境を使って静的ファイルを作成します。
(ブラウザに返すところをHTMLファイル出力に方向転換しただけ。)
静的ページを作る作業 -> ビルド
静的ページをサーバーに置く作業 -> デプロイ
『SSRの静的ページ化』でサイト表示が速い。
プリレンダリングのメリット
Webのリクエストでは、サーバーからCGIを通したプログラムもいらないし、データベースもいりません。
Webサーバーは置いてあるHTMLファイルを返すだけなのでサーバー負荷は小さいです。
(もちろん、ファイルを返すだけなので爆速。)
動的コンテンツはajaxなどの非同期リクエストで対応すればいいでしょう。当然ですがそのときは
Webサーバー
PHPなどのプログラム
データベース
の連携が必要です。
HTMLファイルの中身はレンダリング済みHTMLなので、ふつうの静的ページのHTMLファイルよりもブラウザの表示は速い。
プリレンダリングの使いどころ
サイトのページ数が少ないもの、動的処理があまりない、シンプルなサイトはプリレンダリング一択でしょう。
(コーポレーションサイトとか)
ページ数が多くても、その割に更新頻度が大したことのないサイトにも向いています。
(ブログサイトもイケるかも。チャレンジする価値あり。)
また、SPA, SSRとのハイブリッド構成もできます。Next.js, Nuxt.jsでは、ページごとにプリレンダリングかSSRを選ぶことができます。
プリレンダリングのデメリット
ページ数が多いもの、動的コンテンツの割合が多いものには向きません。
あくまで静的ページなので、htmlファイルの数には限界があります。静的ページのためにディスク領域を100G, 200Gも用意するのは現実的ではありません。
そもそもそのボリュームでビルドしたらどのくらい時間がかかるか...
やってみるのはいいですが、いつか限界が来るでしょう。
また、動的コンテンツが認証機能だけとかならいいですが、動的コンテンツがメインのものはSPA, SSRのほうがいいです。
SSGのビルドの自動化
SSGでビルドして、静的ファイルをWebサーバーへデプロイする一連の作業を自動化することができます。
CIツールは(Continuous Integration, 継続的インテグレーション)のことで、
開発 -> テスト -> ビルド -> デプロイ
の開発サイクルを自動化するツールです。Jenkinsはオープンソースなので無料で『これでもか!』っていうくらいなんでもできます。
(ただし、管理画面で操作するにしては学習コストが高い。)
また、JavaベースのWebアプリなので、JDK? JRE? tomcat??? という人は構築に時間がかかるでしょう。
ほかにも、CIツールはたくさんあります。有料のクラウド・サービスなどは設定するだけで環境構築はいりません。
コードをGitで管理していて、タイマーでビルド・デプロイするならCIツールなしでもできます。
WebサーバーのあるマシンでGitクローン作成。
Gitフェッチ・Webサーバーへファイルコピー(デプロイ)のシェル作成。
シェルをsystemdのタイマーで設定
シェルは5行もいらないでしょう。かんたんです。
Webhookはhttpリクエストで通知するもので、Gitを提供しているホスティングサービスの多くは対応している。
Gitの機能ではなく、Slackなどのビジネス・コミュニケーションツールでも使われている。
まとめ
SPA, SSR, プリレンダリングで、これがベストというものはありません。ケース・バイ・ケースで選んでいくことになります。
(結果、通常の動的ページを選んでもいい。)
情報を発信する。 初期表示スピード大事。 ページ数の変更が少なく、動的コンテンツも少ない。 規模も大したことない。 | コーポレートサイト | SSG + 自動ビルド・デプロイ |
一旦サイトを開くと、長時間、開きっぱなしにする。 ツールとして使うので初期表示の遅さは気にならない。 利用者が多い。(サイトアクセスが多い) | Webアプリ。 Webサービス | SPA |
情報を発信する。 初期表示スピード大事。 ページ数の変更が多い。 | ブログサイト。 ニュースサイト。 | SSG + 自動ビルド・デプロイにチャレンジ。 またはSSR。 結局、通常の動的ページの可能性あり。 |
何かを販売する。 情報を発信する。 初期表示スピード大事。 動的コンテンツの更新が頻繁。 | ECサイト | SSR |
ファイルでコンテンツを提供するのが主。 初期表示のスピード大事。 動的コンテンツが少ない。 | 動画配信サイト | SSG + 自動ビルド・デプロイ。 デプロイの頻度が耐えられないならSSR。 結局、通常の動的ページの可能性あり。 |
ボクの考えではこんなところです。
今回は基本的なイメージを作るためなので、ひとつのサーバーにサイトで必要なアプリを突っ込んでいます。
Dockerなど仮想技術を使う場合は、DBサーバー、Webサーバー、リバースプロキシなど、それぞれコンテナを作って、仮想的にサーバーを分離したりします。
それを入れるとイメージしづらいので外しました。
コメント