SSG is compilerYuta Ide (@sadnessOjisan)JSConfJP2021
View Slide
SSG is like a magic
SSG is your friend
Agenda1. SSG is useful2. Inside SSG3. Look back SSR
Is SSG only forBlog / LP / Q&A ?
言葉の定義 - SST - CSR - SSR - SSG n番煎じな話なので詳細には立ち入りません。 それぞれのパフォーマンス上の特性は @takepepe さんの 「より速いWebを目指すNext.js」にまとまっており、こちらを参照してください。 また、これらは完全に分離できるものでもないことを断っておきます。 ex) template に react を CDN 経由で読み込んだものはどれ?https://speakerdeck.com/takefumiyoshii/nextjs-make-the-web-faster
SST: Server Side Templating - Server で HTML Template に値を埋め込み、HTML を生成してクライアントへ返す - 素直な構成だと TTFB の遅さが問題になりがち - 一方で後述するCSRと比較し、通信の往復は減る
CSR: Client Side Rendering - サーバーから取得したデータを元に、コンテンツの描画をクライアントサイドで完結させる - TTFB は速いが、LCP を悪化させる要因を含む - コンテンツ描画のためにはクライアントでのデータ取得が必要 - HTML / Styling を生成する仕事はクライアントが担う - コンテンツのアップデートにはHTMLを取得しなくていいため SST より効率的になる
SSR: Server Side Rendering - サーバーからテンプレートに値が埋め込まれたHTMLを受け取る - クライアントでそのHTMLは hydration される - hydration 後は CSR できる - SST における TTFB 周りのデメリットは引き継ぐが、CSRの良いところを享受できる
(re)hydration - dehydration - JS世界にあるVDOM を HTML に変換する処理 - SSR サーバーがクライアントにHTMLを返す - (re)hydration - HTML を JS の世界で使えるように戻す処理 - Client ライブラリが実行する
SSG: Static Site Generation - サーバーはリクエストに対してHTMLを返すだけ - HTMLをリクエストより前に生成しておく - ビルド時の静的コンテンツとして固定されるデメリットはある - しかしツールによっては hydration してdynamic な性質を取り込み、そのデメリットを回避する
SSG: Static Site Generation - 歴史は長い - 2000年前半 Database Driven な Site への対抗として SSG が注目される - props: DBアクセスの削減 / TTFB の削減 - cons: ページ数に応じたビルドコスト - これまでに多くのツールが開発されている - 興味がある人は jamstack.org にあるまとめサイトをチェック https://jamstack.org/generators/
SSG: Static Site Generation 現代的なSSG 伝統的なSSG コンテンツから HTML を作る SSG したものを hydration する
伝統的なSSG - テンプレートからHTMLファイルを生成するツール - Markdown ドキュメント、画像などのアセットを元にコンテンツページを作り出せる - 任意の言語が利用できる
現代的なSSG - SSR FW や JS ツールチェインの上に作られる - hydration + CSR の要素がある JS ツールチェインの上に作られることで、transpiler, bundler,compressor などの恩恵が受けられる ex) Next, Gatsby, Nuxt, …
hydration ≠ JS FW - wasm によって任意の言語で SSR + hydration が可能 - 技術的には hydration 付きの SSG も可能である
SSG に対する批判 全てが静的に決まらないと使えない Twitter を SSG で作れますか? 現代的なSSGでCSRすれば対処できるケースも多い EC の商品ページを静的生成、在庫はCSRで取得
Headless Comerce https://www.gatsbyjs.com/solutions/shopify
SSG に対する批判 全てが静的に決まらないと使えない Twitter を SSG で作れますか? 現代的なSSGでCSRすれば対処できるケースも多い EC の商品ページを静的生成、在庫はCSRで取得 要件によってはCSRでも難しい C to C のECでページ作成が頻繁、通報された商品はページごとは消したい
Look back SSR later
SSG is meta-compiler - Web performance 101 - 2017年の Gatsby 作者のブログ - Gatsby is meta-compiler - Gatsby は普通のサイトを速いサイトへと変換する - Gatsby は webpack の設定ファイルを生成している https://www.gatsbyjs.com/blog/2017-09-13-why-is-gatsby-so-fast/
Let's dive into SSG
Chunk - SPA の問題の一つに bundle した JS が巨大過ぎるという問題 - 必要なページに必要なパーツさえあればいいよね → chunk という単位でJSを分割する
大量の script は chunk が理由 - 必要なページで、必要なchunkだけを使う - chunk は bundler の設定で作る
複雑な名前はchunkの規則 - ${key}-${hash} - app-44f67102fd3fe7d6ed63.js - component---src-pages-index-jsx-b3759d3c0476f9883dba.js - commons-fa6c875ea4a0d1a317de.js - styles.80e97039b81d20471ead.css - hash があるとビルドごとにキャッシュを破棄できる
chunk の作り方 (webpack の場合) - chunk 名に対して条件を書く - 条件設定の指針 - ページごとに分ける - 共通ライブラリをくくり出す - 詳しくは @mizchi さんの「webpack chunk 最適 テクニック」 https://qiita.com/mizchi/items/418be9abee5f785696f0
Gatsbyはどのような chunk か - `framework-${hash}` - React などの明らかにどのコンポーネントからも使うライブラリを共通のchunk にまとめる
Gatsbyはどのような chunk か - `lib-${hash}` - module が 160kb を超えると別 chunk
Gatsbyはどのような chunk か - ページごとに chunk - 共通モジュールは chunk - 重いライブラリは chunk - スタイリング系もchunk Improved Next.js and Gatsby page load performance with granularchunking https://web.dev/granular-chunking-nextjs/
navigate - 現代的な SSG では CSR として遷移できる - ではなく - ex) @gatsby/reach-router - 遷移先の chunk が必要
prefetch - resource の先読み - A から B に遷移する前に B の chunk があれば即座に遷移可能 - Gatsby の場合、 に hover したら chunk を取ってこれる
Image optimaization - ビルド時に画像とHTMLを最適化 - Size, Resolution - Tag: picture, source, lazy load - Traced SVG: 小サイズのSVG placeholder を生成、画像ロード時に差し替え - JSConfJP の Speaker が分かりやすい - https://jsconf.jp/2021/speakers/
Zero Runtime CSS in JS - VDOMからのスタイル生成はクライアントで行われる - React の style - CSS in JS lib: (ex) styled-component, emotion, - LCP に影響あり - とはいえほとんどの場合は大丈夫 - ビルド時にCSSをHTMLに埋め込む - zero runtime xxx が流行 “静的に埋め込めるものは事前に埋め込んでしまおう” - ex) linaria, vanila-extract
Gatsby での Zero Runtime CSS in JS の実現 - ビルド時に勝手に HTML に埋め込んでくれる(便利) - ただしランタイムでCSSを書き換える時は、CSSin JS ライブラリ環境下では対応するプラグインが必要 - gatsby-plugin-emotion など
How about Performance
手書きHTMLが最速? - 個人的な誤解: React / Next / Gatsby を使えば早くなる - They say “diffing”, “blazing fast”, … - 駆け出しエンジニア時代、SPA はすごいことをしているという誤解をする - 【翻訳】 2016年にJavaScriptを学んでどう感じたか ← これめちゃくちゃ面白い - https://www.fendo181.me/entry/2016/10/26/172404 - 冷静に考えると・・・ - 差分検知なんぞせず、手で直接実DOMを部分更新した方がエコでは? - そもそもランタイムにReactライブラリを含めない方がエコでは?
HandwrittenVSGatsby
Handwritten must have advantagein simple condition
Hello World だけを比較 手書きHTML Gatsby ランタイムのJSが遅れるものの、FCPには影響がない。
手書きが勝てない理由, FCP に影響がない理由 - SSG (=静的化)しているのだから HTML に支払うコストは同じになる - ランタイムが膨らむと言っても chunk へのリンクが挟まれているだけで、それはasync load されるのでブロッキングされない - Gatsby のビルドは自然と HTML が minify される
Gatsby に嫌がらせをしてみた - Gatsby の遷移は CSR としての遷移 - つまり、遷移を妨害すれば良い - 遷移先の chunk を巨大にする - 嫌がらせ chunk が分離されないように 1pagecomponent にベタ書く - 巨大 prefetch 中に遷移する 20MB のDOM要素を用意しましたheavy_heavy_heavy_…
Gatsby に嫌がらせをしてみた 手書きHTMLなら、HTMLごと遷移すれば先頭の表示はされる Gatsby の場合、固まる やったね!
あ ほ く さ
SSGした方が良い? - SPA (1HTML しかない CSR) に対してはライブラリの分の容量分のアドバンテージがある - SSG に対しては、SSG 側もライブラリ分の容量を HTML からは削るので差が出にくい - ランタイムで動かしたいJSがあっても(ex カルーセル、モーダル)、別チャンクに切り出せば HTML を読み込むコストとしての差は出ない - 別コンポーネントに分けて、2カ所以上から読み込む SSGした方が良い
SSR, CSR の弱点、なぜ SSG は速いのか - 一番効いてるのは事前生成によるTTFBの削減 - SSR はここが弱点 - I/O - CPU負荷
SSG の弱点 大量コンテンツのビルド Incremental Build コンテンツの追加・削除・編集 ISR DSG SSR + CDN 弱点 対策
Incremental Build - Gatsby のビルドモードの一つ - ビルド時の cache を使って、次回は差分のみをビルド - Gatsby Cloud でしか使えなかったが v3 でオープンに - とはいえ問題が根本的に解消はされない - うっかり cache を消した時のリカバリは? - cache 全体に影響があるような変更が入ると? - 運用者目線ではすぐにデプロイし直せる保証が欲しい - DSG (後述)
DSG: Deferred Static Generation - Gatsby v4~ - ビルドタイミングを制御する機能 - 静的ビルドするパスを宣言できる - ビルド時に生成しなかったページはユーザーのアクセス時に作られる = SSR - CDNレイヤーにcache - アクセスされたページの HTML が static/ フォルダに作られるわけではない - Gatsby Cloud を使う必要がある - 使わなくても良い方法があるが、それは後述する方法と同じやり方 https://www.gatsbyjs.com/docs/conceptual/rendering-options/
ISR: Incremental Static Regeneration - NextJS のモードの一つ - SSR と SSG のいいところ取り - 一度 SSR した HTML をキャッシュし、次回のアクセスからそれを返す - 処理軽減 - IO削減 - stale な cache を返しつつ cache を fresh にできる - (基本的には)Vercel 環境でしか動かせない
That’s lock-in
lock-in が嫌われている気がする - Vercel や Gatsby が素晴らしい機能を出すたびに lock-in が心配される - 個人的には 「Vercel に乗っかりなよ」とは思うが、lock-in されたくない気持ちも理解できる - よし、内製しよう - DSG / ISR のような動作は CDN で担保する - SSR FW を自作すればいい
SSR + CDN - SSR した HTML を CDN に cache - 一番素直で FW や Hosting 環境にしばられないやり方 - cache の制御もしやすい - stale-while-revalidate => ISR - vary - dynamic cache purging
FW を使わない SSR -とある実装を参考に- - Best practice component library を作っておく - ビルド時にクライアントコードの最適化 - chunk の作成 - linaria の実行 - NodeJS サーバーのエンドポイントで リクエストを待ち受ける - (p) react をサーバーで実行して HTML を作成 - cache control header を設定して返す 意外とシンプル preactとfastifyでSSRhttps://zenn.dev/takurinton/articles/4c8625a43f024b
FW を使わないメリット - ランタイムライブラリの選択が自由 - 不都合なく preact が使える - _app.js, gatsby-browser.js より細かい粒度で共通設定を書ける - ビルドチェインの拡張が容易 - React app 以外のコードをビルド、SSR時に script tag で挟み込むといったことがやりやすい - 1 サービスを複数チームで育てる時に管理しやすい - partial hydration - とはいえ Astro, Next12 も凄い!
FW を使わないデメリット、その指摘 - FW を作ることに消耗したくない、案件を進めたい - Next の完コピを目指すと大変だけど、ただのSSRサーバーを作るだけならそこまでコストはない - バックエンドの扱いは慣れていない - cache hit ratio を上げる ※ と、ここまで偉そうに話しましたがほとんどが知人の実装やおかげです
まとめ - 現代的な SSG は dynamic な要件に対応できる - SSG が内部でしていることはページの事前生成とパフォーマンスの最適化 - SSG の辛さへの回答として、SSR で HTML を Generate して CDN でCache - FW や PFM へのロックインが気になるなら自作できる