このブログ兼 portfolio サイトを prerendering で実装しました。
prerendering 実装したのは初めてだったので備忘録としても残しておきます。
prerendering とは、web アプリケーションのレンダリングの一つの方法です。
通常、Reactなどで作られたアプリケーションでは、コンテンツがない空の HTML をサーバーから受け取り、その後JSを読み込んでコンテンツを表示します。
この prerendering では、アプリケーションを build 時にレンダリングしてコンテンツ入りの HTML を作っておきます。クライアントはその build 済みの HTML をサーバーから受け取ることのみでページを表示する事ができます。
そうする事で、TTFB を早めることや、Google などの bot に対して確実にコンテンツ入りの HTML を返せる事などができます。
また、GatsbyJS などはこの prerendering の技術をとりいれています。
今回はこの prerendering を行なってコンテンツ入りの HTML を表示したのちに JS を読み込み、通常のSPAのアプリとして動作するような実装をしました。
使用した技術は次のようになります。
react, styled-components, puppeteer, webpack, express, npm script
react prerender などで調べると react-snap を使用した例が多く出てくるため最初はこれで作ろうと思いましたが、create-react-app で作られたアプリだと zero-config で prerendering できるのですが、create-react-app を使用していないため簡単に適用できませんでした。
そのため react-snap の内部でも使用している puppeteer を用いて実装しました。
実装は npm script
で command を作成し、そのコマンド一つで prerendering された html ファイルを作成できるようにしました。
ディレクトリの構成はこのような感じです。
clients/
src/
dist/
webpack.config.js
scripts/
prerender/
clients
配下は通常の React Application になっていて、scripts
内に npm script で実行するファイルを保存、prerender
内に prerendering 後の html ファイルを保存しています。
prerendering された HTML を作成する script は以下のような内容になってます:
client/src
配下 (react) を build した結果を dist
配下に配置 (html と js) client/src
ディレクトリをサーブするprerender
配下に置くまた、puppeteer ではスクリーンショットを撮ることが可能なため、各ページのスクリーンショットを作成し、OGP用のイメージとして保存しています。
styled-components ではスタイルを head タグ内に style タグを作成しその中にスタイルを定義しているのですが、パフォーマンス向上のため CSSOM を直接 DOM に読み込ませるということをしています。
そのため document.documentElement.innerHTML
などの関数では style タグ内を取得できず、chrome dev tool の element tab などでも style タグ内を確認することができません。
そのままでは puppeteer でも同様に style タグの値を取得できないのですが、最近リリースされた styled-components v5 で導入された disableCSSOMInjection を使用することで回避することができました。
これを使用することで CSSOM をそのまま挿入するのではなく、テキストベースの CSS を DOM に追加する処理に変わるため、puppeteer で style の情報を取得しスタイル込みの HTML を作成できました。
chrome でページソースを見てみると、ちゃんとレンダリングされてることがわかります。
before
after
今回 prerendering した HTML は skelton などではなく完全なコンテンツ入りのHTMLなので、単純な HTML のみで SPA にしない方がパフォーマンスいいかもしれないのでそれも試してみたい。