Gatsbyの静的ページ遅延生成機能であるDSGを使ってビルド時間を短縮してみた。
どうも、ベルリンオフィスの小西です。
本日はGatsbyのビルド時間を短縮する仕組み DSG を使ってみたので紹介します。
GatsbyのDSGってなぁに?
DSG (Deferred Static Generation)はGatsbyのバージョン4から追加された機能で、一部のページの生成を、ビルド時ではなくアクセスがあって初めて行います。
自分は 静的ページ遅延生成機能 と訳すとしっくりきました。
Gatsbyは静的ページ生成フレームワークのため、基本的にサイトビルド時に全ての静的ページを生成します。
これによりユーザーがどのページをリクエストしても高速なページの表示が可能になるのですが、一方でページ数が増えるに従ってビルド時間も比例して増えていくことになります。
数百ページ程度なら問題になることはないと思いますが、ヘッドレスCMSなどを使って数千ページ以上の規模になってくると、一回一回のビルドでの待ち時間のロスが大きくなります。
DSGはこのビルド時間の問題に対する解決法です。
一部の指定したページはビルド時に生成されず(その分ビルド時間が短くなり)、ユーザーがアクセスした際に生成するようになります。
DSGを使ってみる
早速DSGを使ってみます。
設定は非常に簡単でgatsby-node.js
のcreatePage()
関数のオプションdefer
を使用します。
さくっとGatsbyを立ち上げ。
% gatsby new hello-world % cd hello-world
アプリルート直下に生成された gatsby-node.js
を開きます。
exports.createPages = async ({ actions }) => { const { createPage } = actions createPage({ path: "/using-dsg", component: require.resolve("./src/templates/using-dsg.js"), context: {}, defer: true, }) }
最新のGatsbyだとすでにDSGのテンプレートが記述されていますね。なんて親切。
defer
がtrueの場合、それらのページを遅延生成します。
Gatsbyビルドログで確認してみる。
アクセスするまでページが生成されていないかをみてみます。
まずgatsby-node.js
はそのままで、ビルドを行ってみます。
(注意: DSGはgatsby build
コマンドでは機能しません。下記のように build
&server
コマンドを実行してプロダクションサーバーで実行する必要があります)
% gatsby clean % gatsby build --verbose ~(ビルドログ中略)~ Pages ┌ src/templates/using-dsg.js │ └ D /using-dsg ├ src/pages/404.js │ ├ /404/ │ └ /404.html ├ src/pages/index.js │ └ / ├ src/pages/page-2.js │ └ /page-2/ ├ src/pages/using-ssr.js │ └ ∞ /using-ssr/ └ src/pages/using-typescript.tsx └ /using-typescript/ ╭────────────────────────────────────────────────────────────────╮ │ │ │ (SSG) Generated at build time │ │ D (DSG) Deferred static generation - page generated at runtime │ │ ∞ (SSR) Server-side renders at runtime (uses getServerData) │ │ λ (Function) Gatsby function │ │ │ ╰────────────────────────────────────────────────────────────────╯
/using-dsg
の前に「D」マークがありますが、これはDSGの対象(まだこの時点で生成されていないこと)を指します。
この時点で public/page-data
ディレクトリ配下をみても /using-dsg
のディレクトリが生成されていないはずです。
試しにこのビルドしたデータでサイトを立ち上げてみます。
% gatsby serve
その後ブラウザで http://localhost:9000/using-dsg にアクセスしてみるとページが表示されます。
今度は先のオプションdefer
をfalse
にして、DSGしないパターンのビルドログをみてみます。
exports.createPages = async ({ actions }) => { const { createPage } = actions createPage({ path: "/using-dsg", component: require.resolve("./src/templates/using-dsg.js"), context: {}, defer: false, }) }
再度キャッシュなどクリアしてビルドします。
% gatsby clean % gatsby build --verbose ~(ビルドログ中略)~ Pages ┌ src/templates/using-dsg.js │ └ /using-dsg ├ src/pages/404.js │ ├ /404/ │ └ /404.html ├ src/pages/index.js │ └ / ├ src/pages/page-2.js │ └ /page-2/ ├ src/pages/using-ssr.js │ └ ∞ /using-ssr/ └ src/pages/using-typescript.tsx └ /using-typescript/ ╭────────────────────────────────────────────────────────────────╮ │ │ │ (SSG) Generated at build time │ │ D (DSG) Deferred static generation - page generated at runtime │ │ ∞ (SSR) Server-side renders at runtime (uses getServerData) │ │ λ (Function) Gatsby function │ │ │ ╰────────────────────────────────────────────────────────────────╯
今度は Pages 内の /using-dsg の前に「D」がついていません。
またビルドした直後でも public/page-data/
ディレクトリ内に using-dsg
が生成されていますね。これが遅延生成されていないページのふるまいになります。
DSGの使いみち
DSGは全てのページに使うものではなく、「アクセスが少なく重要度も低いけれどもビルド時間を食っているコンテンツ」に対して使うのが正です(Gatsby流にいうなら”non-critical page”に対して使うもの)。
例えばアクセスの少ない過去のブログ記事、バージョニングされたドキュメントの非最新バージョンなどに利用が検討できると思います。
ユーザーにとっての利便性と開発体験のバランスを考えて使えばいいかと思います。
DSGされたページのキャッシュ
一度生成されたページはキャッシュされ、以後通常のSSG(静的生成)されたページと同様にふるまいます。
Gatsby Cloud(裏側ではCDNとしてFastlyが走っている)でホスティングする場合、DSGされたページは自動的にキャッシュされます(Gatsby公式のアナウンスでは、DSGを完全に利用するには現時点ではGatsby Cloudが推奨とのことです)。
DSGはIncremental Static Regeneration (ISR)とどう違う?
Gatsbyによると、DSGで遅延生成されるページは、サーバー側のAPI呼び出しではなく、他のページと同じビルドデータのキャッシュで生成されるため、データの不整合が起きにくいとしています。
例えばビルドが月曜日にデプロイされ、遅延ページが金曜日になって初めてリクエストされた際、月曜日のデプロイのデータを使用してそのページはビルドされます。
そういう意味でNext.jsのISRはSSRに近い仕組みになるため、DSGのほうがより安定している(Less britle)と説明しています。