Slide 1

Slide 1 text

SSG is compiler Yuta Ide (@sadnessOjisan) JSConfJP2021

Slide 2

Slide 2 text

SSG is like a magic

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

SSG is your friend

Slide 8

Slide 8 text

Agenda 1. SSG is useful 2. Inside SSG 3. Look back SSR

Slide 9

Slide 9 text

Is SSG only for Blog / LP / Q&A ?

Slide 10

Slide 10 text

言葉の定義
 - SST
 - CSR
 - SSR
 - SSG 
 
 
 n番煎じな話なので詳細には立ち入りません。 
 それぞれのパフォーマンス上の特性は @takepepe さんの 「より速いWebを目指す Next.js」にまとまっており、こちらを参照してください。 
 また、これらは完全に分離できるものでもないことを断っておきます。 
 ex) template に react を CDN 経由で読み込んだものはどれ? https://speakerdeck.com/takefumiyoshii/nextjs-make-the-web-faster

Slide 11

Slide 11 text

SST: Server Side Templating
 - Server で HTML Template に値を埋め込み、HTML を生成してクライアントへ返す
 - 素直な構成だと TTFB の遅さが問題になりがち
 - 一方で後述するCSRと比較し、通信の往復は減る


Slide 12

Slide 12 text

CSR: Client Side Rendering
 - サーバーから取得したデータを元に、コンテンツの 描画をクライアントサイドで完結させる
 - TTFB は速いが、LCP を悪化させる要因を含む
 - コンテンツ描画のためにはクライアントでのデータ取得が 必要
 - HTML / Styling を生成する仕事はクライアントが担う 
 - コンテンツのアップデートにはHTMLを取得しなくて いいため SST より効率的になる


Slide 13

Slide 13 text

SSR: Server Side Rendering
 - サーバーからテンプレートに値が埋め込まれた HTMLを受け取る
 - クライアントでそのHTMLは hydration される
 - hydration 後は CSR できる
 - SST における TTFB 周りのデメリットは引き継 ぐが、CSRの良いところを享受できる


Slide 14

Slide 14 text

(re)hydration
 - dehydration
 - JS世界にあるVDOM を HTML に変換する処理 
 - SSR サーバーがクライアントにHTMLを返す 
 
 - (re)hydration
 - HTML を JS の世界で使えるように戻す処理 
 - Client ライブラリが実行する 


Slide 15

Slide 15 text

SSG: Static Site Generation
 - サーバーはリクエストに対してHTMLを返すだ け
 - HTMLをリクエストより前に生成しておく
 - ビルド時の静的コンテンツとして固定されるデメ リットはある
 - しかしツールによっては hydration して dynamic な性質を取り込み、そのデメリットを回 避する 


Slide 16

Slide 16 text

SSG: Static Site Generation
 - 歴史は長い
 - 2000年前半 Database Driven な Site への対 抗として SSG が注目される
 - props: DBアクセスの削減 / TTFB の削減 
 - cons: ページ数に応じたビルドコスト 
 - これまでに多くのツールが開発されている
 - 興味がある人は jamstack.org にあるまとめサイトを チェック 
 https://jamstack.org/generators/ 


Slide 17

Slide 17 text

SSG: Static Site Generation
 現代的なSSG
 伝統的なSSG
 コンテンツから HTML を作る SSG したものを hydration する


Slide 18

Slide 18 text

伝統的なSSG
 - テンプレートからHTMLファイルを生成するツール
 - Markdown ドキュメント、画像などのアセットを元にコンテンツページを作り出せ る
 - 任意の言語が利用できる


Slide 19

Slide 19 text

現代的なSSG
 - SSR FW や JS ツールチェインの上に作られる
 - hydration + CSR の要素がある
 JS ツールチェインの上に作られることで、transpiler, bundler, compressor などの恩恵が受けられる 
 ex) Next, Gatsby, Nuxt, … 


Slide 20

Slide 20 text

hydration ≠ JS FW
 - wasm によって任意の言語で SSR + hydration が可能
 - 技術的には hydration 付きの SSG も可能である


Slide 21

Slide 21 text

SSG に対する批判
 全てが静的に決まらないと使えない
 Twitter を SSG で作れますか? 
 現代的なSSGでCSRすれば対処できるケースも多い
 EC の商品ページを静的生成、在庫はCSRで取得 


Slide 22

Slide 22 text

Headless Comerce
 https://www.gatsbyjs.com/solutions/shopify

Slide 23

Slide 23 text

SSG に対する批判
 全てが静的に決まらないと使えない
 Twitter を SSG で作れますか? 
 現代的なSSGでCSRすれば対処できるケースも多い
 EC の商品ページを静的生成、在庫はCSRで取得 
 要件によってはCSRでも難しい
 C to C のECでページ作成が頻繁、通報された商品はページごとは消したい 


Slide 24

Slide 24 text

Look back SSR later

Slide 25

Slide 25 text

Agenda 1. SSG is useful 2. Inside SSG 3. Look back SSR

Slide 26

Slide 26 text

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/

Slide 27

Slide 27 text

Let's dive into SSG

Slide 28

Slide 28 text

Chunk
 - SPA の問題の一つに bundle した JS が巨大過ぎるという問題
 - 必要なページに必要なパーツさえあればいいよね
 → chunk という単位でJSを分割する


Slide 29

Slide 29 text

大量の script は chunk が理由
 - 必要なページで、必要なchunk だけを使う
 - chunk は bundler の設定で作 る


Slide 30

Slide 30 text

複雑な名前はchunkの規則
 - ${key}-${hash}
 - app-44f67102fd3fe7d6ed63.js 
 - component---src-pages-inde x-jsx-b3759d3c0476f9883dba. js
 - commons-fa6c875ea4a0d1a31 7de.js
 - styles.80e97039b81d20471ead .css
 - hash があるとビルドごとに キャッシュを破棄できる


Slide 31

Slide 31 text

chunk の作り方 (webpack の場合)
 - chunk 名に対して条件を書く
 - 条件設定の指針
 - ページごとに分ける 
 - 共通ライブラリをくくり出す 
 - 詳しくは @mizchi さんの「webpack chunk 最適 テク ニック」
 https://qiita.com/mizchi/items/418be9abee5f785696f0

Slide 32

Slide 32 text

Gatsbyはどのような chunk か
 - `framework-${hash}`
 - React などの明らかにどのコンポーネントからも使うライブラリを共通の chunk にまとめる


Slide 33

Slide 33 text

Gatsbyはどのような chunk か
 - `lib-${hash}`
 - module が 160kb を超えると別 chunk


Slide 34

Slide 34 text

Gatsbyはどのような chunk か
 - ページごとに chunk
 - 共通モジュールは chunk
 - 重いライブラリは chunk
 - スタイリング系もchunk
 Improved Next.js and Gatsby page load performance with granular chunking
 https://web.dev/granular-chunking-nextjs/

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

navigate
 - 現代的な SSG では CSR として遷移できる
 - ではなく 
 - ex) @gatsby/reach-router 
 - 遷移先の chunk が必要


Slide 37

Slide 37 text

prefetch
 - resource の先読み
 - A から B に遷移する前に B の chunk があれば即座に遷移可能
 - Gatsby の場合、 に hover したら chunk を取ってこれる


Slide 38

Slide 38 text

Image optimaization
 - ビルド時に画像とHTMLを最適化
 - Size, Resolution
 - Tag: picture, source, lazy load 
 - Traced SVG: 小サイズのSVG placeholder を生成、画 像ロード時に差し替え 
 - JSConfJP の Speaker が分かりやすい
 - https://jsconf.jp/2021/speakers/ 


Slide 39

Slide 39 text

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 


Slide 40

Slide 40 text

Gatsby での Zero Runtime CSS in JS の実現
 - ビルド時に勝手に HTML に埋め込んでくれる (便利)
 - ただしランタイムでCSSを書き換える時は、CSS in JS ライブラリ環境下では対応するプラグイン が必要
 - gatsby-plugin-emotion など 


Slide 41

Slide 41 text

How about Performance

Slide 42

Slide 42 text

手書きHTMLが最速?
 - 個人的な誤解: React / Next / Gatsby を使えば早くなる
 - They say “diffing”, “blazing fast”, … 
 - 駆け出しエンジニア時代、SPA はすごいことをしているという誤解をする 
 - 【翻訳】 2016年にJavaScriptを学んでどう感じたか ← これめちゃくちゃ面白い 
 - https://www.fendo181.me/entry/2016/10/26/172404 
 - 冷静に考えると・・・
 - 差分検知なんぞせず、手で直接実DOMを部分更新した方がエコでは? 
 - そもそもランタイムにReactライブラリを含めない方がエコでは? 


Slide 43

Slide 43 text

Handwritten VS Gatsby

Slide 44

Slide 44 text

Handwritten must have advantage in simple condition

Slide 45

Slide 45 text

Hello World

だけを比較
 手書きHTML
 Gatsby
 ランタイムのJSが遅れるものの、FCPには影響がない。


Slide 46

Slide 46 text

手書きが勝てない理由, FCP に影響がない理由
 - SSG (=静的化)しているのだから HTML に支払うコストは同じになる
 - ランタイムが膨らむと言っても chunk へのリンクが挟まれているだけで、それは async load されるのでブロッキングされない
 - Gatsby のビルドは自然と HTML が minify される


Slide 47

Slide 47 text

Gatsby に嫌がらせをしてみた
 - Gatsby の遷移は CSR としての遷移
 - つまり、遷移を妨害すれば良い
 - 遷移先の chunk を巨大にする 
 - 嫌がらせ chunk が分離されないように 1page component にベタ書く 
 - 巨大 prefetch 中に遷移する 
 20MB のDOM要素を用意しました

heavy_heavy_heavy_…

Slide 48

Slide 48 text

Gatsby に嫌がらせをしてみた
 手書きHTMLなら、HTMLごと遷移すれ ば先頭の表示はされる 
 Gatsby の場合、固まる 
 
 やったね!


Slide 49

Slide 49 text

あ
 ほ
 く
 さ


Slide 50

Slide 50 text

SSGした方が良い?
 - SPA (1HTML しかない CSR) に対してはライブラリの分の容量分のアドバンテー ジがある
 - SSG に対しては、SSG 側もライブラリ分の容量を HTML からは削るので差が出 にくい
 - ランタイムで動かしたいJSがあっても(ex カルーセル、モーダル)、別チャンクに 切り出せば HTML を読み込むコストとしての差は出ない
 - 別コンポーネントに分けて、2カ所以上から読み込む 
 SSGした方が良い

Slide 51

Slide 51 text

Agenda 1. SSG is useful 2. Inside SSG 3. Look back SSR

Slide 52

Slide 52 text

SSR, CSR の弱点、なぜ SSG は速いのか
 - 一番効いてるのは事前生成によるTTFBの削減
 - SSR はここが弱点
 - I/O
 - CPU負荷


Slide 53

Slide 53 text

SSG の弱点
 大量コンテンツのビルド
 Incremental Build
 コンテンツの追加・削除・編集
 ISR
 DSG
 SSR + CDN
 弱点 対策

Slide 54

Slide 54 text

Incremental Build
 - Gatsby のビルドモードの一つ
 - ビルド時の cache を使って、次回は差分のみをビルド
 - Gatsby Cloud でしか使えなかったが v3 でオープンに
 - とはいえ問題が根本的に解消はされない
 - うっかり cache を消した時のリカバリは? 
 - cache 全体に影響があるような変更が入ると? 
 - 運用者目線ではすぐにデプロイし直せる保証が欲しい 
 - DSG (後述)


Slide 55

Slide 55 text

DSG: Deferred Static Generation
 - Gatsby v4~
 - ビルドタイミングを制御する機能
 - 静的ビルドするパスを宣言できる 
 - ビルド時に生成しなかったページはユーザーの アクセス時に作られる = SSR 
 - CDNレイヤーにcache
 - アクセスされたページの HTML が static/ フォ ルダに作られるわけではない 
 - Gatsby Cloud を使う必要がある 
 - 使わなくても良い方法があるが、それは後述す る方法と同じやり方
 https://www.gatsbyjs.com/docs/conceptual/rendering-options/

Slide 56

Slide 56 text

ISR: Incremental Static Regeneration
 - NextJS のモードの一つ
 - SSR と SSG のいいところ取り
 - 一度 SSR した HTML をキャッシュし、次回のアクセスからそれを返す 
 - 処理軽減
 - IO削減
 - stale な cache を返しつつ cache を fresh にできる 
 - (基本的には)Vercel 環境でしか動かせない


Slide 57

Slide 57 text

That’s lock-in

Slide 58

Slide 58 text

lock-in が嫌われている気がする
 - Vercel や Gatsby が素晴らしい機能を出すたびに lock-in が心配される
 - 個人的には 「Vercel に乗っかりなよ」とは思うが、lock-in されたくない気持 ちも理解できる
 - よし、内製しよう
 - DSG / ISR のような動作は CDN で担保する 
 - SSR FW を自作すればいい 


Slide 59

Slide 59 text

SSR + CDN
 - SSR した HTML を CDN に cache
 - 一番素直で FW や Hosting 環境にし ばられないやり方
 - cache の制御もしやすい
 - stale-while-revalidate => ISR 
 - vary
 - dynamic cache purging 


Slide 60

Slide 60 text

FW を使わない SSR -とある実装を参考に- 
 - Best practice component library を作っておく
 - ビルド時にクライアントコードの最適化
 - chunk の作成
 - linaria の実行
 - NodeJS サーバーのエンドポイントで リクエストを待ち受ける
 - (p) react をサーバーで実行して HTML を作成
 - cache control header を設定して返す
 意外とシンプル
 preactとfastifyでSSR https://zenn.dev/takurinton/articles/4 c8625a43f024b

Slide 61

Slide 61 text

FW を使わないメリット
 - ランタイムライブラリの選択が自由
 - 不都合なく preact が使える 
 - _app.js, gatsby-browser.js より細かい粒度で共通設定を書ける
 - ビルドチェインの拡張が容易
 - React app 以外のコードをビルド、SSR時に script tag で挟み込むといったことがやりや すい
 - 1 サービスを複数チームで育てる時に管理しやすい 
 - partial hydration
 - とはいえ Astro, Next12 も凄い! 


Slide 62

Slide 62 text

FW を使わないデメリット、その指摘
 - FW を作ることに消耗したくない、案件を進めたい
 - Next の完コピを目指すと大変だけど、ただのSSRサーバーを作るだけならそこまでコスト はない
 - バックエンドの扱いは慣れていない
 - cache hit ratio を上げる 
 ※ と、ここまで偉そうに話しましたがほとんどが知人の実装やおかげです

Slide 63

Slide 63 text

まとめ
 - 現代的な SSG は dynamic な要件に対応できる
 - SSG が内部でしていることはページの事前生成とパフォーマンスの最適化
 - SSG の辛さへの回答として、SSR で HTML を Generate して CDN で Cache
 - FW や PFM へのロックインが気になるなら自作できる