Skip to content

Commit fd552c7

Browse files
committed
Update
1 parent d01650c commit fd552c7

5 files changed

Lines changed: 294 additions & 5 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
---
2+
title: Incremental Static Regeneration で実現する次世代のサーバーアーキテクチャ
3+
created: 1589802285779
4+
tags: [next, react, ssg]
5+
---
6+
7+
next.js 9.4 に Incremental Static Regeneration という実験的な新機能があります。
8+
9+
[Blog \- Next\.js 9\.4 \| Next\.js](https://nextjs.org/blog/next-9-4)
10+
11+
パッと見、「段階的な静的サイト生成…?なんのことだろう…」となったのですが、手元で試してみた感じ、これが既存のサーバーの実装アプローチを変える、革命的な機能ではないかと思いました。
12+
13+
解説を書きつつ、どのような応用があるか解説します。
14+
15+
## next.js の Incremental SSG を試してみる
16+
17+
リポジトリはここです。 [mizchi/issg\-playground](https://github.com/mizchi/issg-playground)
18+
19+
解説にあたっては、必要なのはほぼこのファイルだけで、短いのでそのまま貼ります。
20+
21+
```tsx
22+
// pages/[slug].tsx
23+
import { GetStaticProps, GetStaticPaths } from "next";
24+
25+
type Props = {
26+
slug: string;
27+
builtAt: number;
28+
};
29+
30+
export const getStaticProps: GetStaticProps<Props> = async (ctx) => {
31+
return {
32+
props: {
33+
slug: ctx.params.slug as string,
34+
builtAt: Date.now(),
35+
},
36+
unstable_revalidate: 30,
37+
};
38+
};
39+
40+
export const getStaticPaths: GetStaticPaths = async () => {
41+
return {
42+
paths: ["/foo"],
43+
fallback: true,
44+
};
45+
};
46+
47+
export default (props: Props) => {
48+
return (
49+
<>
50+
{props.slug}: {props.builtAt}
51+
</>
52+
);
53+
};
54+
```
55+
56+
- `pages/[slug].tsx``/*` をハンドルします。
57+
- `export const getStaticProps` はそのルーティングに来たときのルート要素に渡す props を組み立てます
58+
- `export const getStaticPaths` はそのルーティングに来たときの、パス一覧を返却します。
59+
60+
これらの機能は元々 `next export` の静的アセットの吐き出しのために、手元のビルド時に一回だけ実行されるものでした。(このブログも、この機能を使って生成されています)
61+
62+
しかし、 Incremental SSG では、このサーバーは静的に吐き出してデプロイされるのではなく、 server or serverless モードでデプロイすることを想定されています。
63+
静的アセットの吐き出しサーバーを、動的なサーバーとしてデプロイする、とはどういうことでしょうか。
64+
65+
ここで、 `unstable_revalidate: 30``fallback: true` に注目してください。
66+
67+
- `getStaticPaths``fallback: true` が指定されると、 `paths` で指定されなかったパスも、 `getStaticProps` のロジックに応じて組み立てられます。
68+
- `getStaticProps``unstable_revalidate: 30` のような値を返すと、 30 秒間は静的アセットとして返却されます
69+
70+
ここからが面白くて
71+
72+
- unstable_revalidate: 30` の秒数が経過後、次のリクエストが発生した際に、一旦はキャッシュを返しつつ、バックグラウンドでもう一度そのページを構築
73+
74+
この挙動が面白いですね。
75+
76+
つまりは、静的サイトジェネレータとしてある程度の運用の容易さを担保しつつ、CDN のスケーラビリティを借りて、かつ、ある程度は動的な振る舞いを取れる、ということです。
77+
78+
完全なアプリケーション・サーバーとしては、
79+
80+
## フロントエンドベストプラクティスの実現
81+
82+
自分は [光を超えるためのフロントエンドアーキテクチャ \- Speaker Deck](https://speakerdeck.com/mizchi/guang-wochao-erutamefalsehurontoendoakitekutiya) という発表をしたことがあります。要約すると、パフォーマンス最適化のためには、リクエストを全部アプリケーション・サーバーに到達させてはだめで、 CDN Edge に置いた HTML に当てつつ、キャッシュごとにサロゲートキーを当てて、リソースの更新のたびにプログラマブルなインバリデーションを発行する、というものです。
83+
84+
当時、これを実現できるのは fastly しかありませんでした。
85+
86+
まだプログラマブルなインバリデーションはないのですが、RFC のディスカッションを読む限りは、rauchg と Timer 曰く、もっと多機能なものも考えているらしいので、 それを想定してるように見えます。
87+
88+
[RFC: Incremental Static Regeneration · Discussion \#11552 · zeit/next\.js](https://github.com/zeit/next.js/discussions/11552)
89+
90+
## Vercel / SmartCDN / Next.js のゴールが見えた
91+
92+
正直なところ、 next.js が静的 export に対応した当時は、プロダクトとしての軸がぶれているように感じました。SSR のフレームワークでいきたいのか、 SSG になりたいのか、どっちなのかと。そして自前の PaaS を運用しているのは、よくわからないところがありました。
93+
94+
Vercel (旧名 now.sh) は SmartCDN という機能があります。これはおそらく、この機能を見据えたプログラマブルな CDN として設計されたものだったのでしょう。
95+
96+
Incremental SSG は、NoCode などの Headless CMS のガワとして、next.js を使うことが想定されています。これらの NoCode Backend はお世辞にもスケーラビリティがあるとは言えないものが多く、またレスポンスタイムに難があることが多かったのですが、Incremental SSG モードの Next.js をかぶせることで(初回アクセスをやや犠牲にしつつも) CDN のスケーラビリティの恩恵を受けることができます。
97+
98+
おそらく Vercel + SmartCDN は、next に最適化された CMS バックエンドとして、オールインワンパッケージを提供するのがゴールなのでしょう。
99+
100+
## 未来
101+
102+
とりあえず RFC に fastly の SurrogateKeys 相当のキャッシュタグみたいなものがほしい!とだけ書いておきました。
103+
104+
[RFC: Incremental Static Regeneration · Discussion \#11552 · zeit/next\.js](https://github.com/zeit/next.js/discussions/11552#discussioncomment-14415)
105+
106+
また、ZEIT, あらため Vercel は 20 億円の増資を受けたことで、next.js エコシステムの発展は、より加速していくように思います。
107+
108+
[\(20\) Shu Uesugi さんは Twitter を使っています 「🆕Next\.js の開発元でもある ZEIT はこのたび社名変更し Vercel になりました。 🎉2100 万ドルの資金調達も発表。 👨🏻‍💻 私はご縁があり 2 月に開発者としてジョインしました。 🤔「○○ は今後どうなるの?」というご質問につきましては、こちらの Notion ドキュメントをご一読ください!→ https://t\.co/eCwc23gIzo」 / Twitter](https://twitter.com/chibicode/status/1252745903540105216)
109+
110+
ちょっと前まで、next.js は意見が強いフレームワークで、正直 nuxt のほうが使いやすいよなぁ、と思ってたんですが、こういう感じで攻めてくるのは以外で、びっくりしつつも、応援したい気持ちがあります。

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"@types/react-dom": "^16.9.7",
2525
"@types/styled-components": "^5.1.0",
2626
"amdx-loader": "^0.8.0",
27-
"amdxg-cli": "^0.8.2",
27+
"amdxg-cli": "^0.8.4",
2828
"raw-loader": "^4.0.1",
2929
"rimraf": "^3.0.2",
3030
"rollup": "^2.9.1",

pages/develop-mizchi-dev.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Head from "next/head";
2+
import { Layout } from "amdxg-components";
3+
// @ts-ignore
4+
import { frontmatter } from "../slides/develop-mizchi-dev.mdx";
5+
// @ts-ignore
6+
import rawMdx from "!raw-loader!../slides/develop-mizchi-dev.mdx";
7+
8+
import config from "../amdxg.config";
9+
import pages from "../gen/pages.json";
10+
import { parse } from "amdx";
11+
12+
export const config = { amp: true };
13+
14+
export default () => {
15+
return (
16+
<>
17+
<Head>
18+
<title>{frontmatter.title}</title>
19+
</Head>
20+
<Layout config={config}></Layout>
21+
</>
22+
);
23+
};

slides/develop-mizchi-dev.mdx

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
title: next.js で自分のブログを作る
3+
---
4+
5+
新しく自分のブログとして [mizchi.dev](https://mizchi.dev) を作った話
6+
7+
---
8+
9+
## Lighthouse
10+
11+
![](https://i.gyazo.com/718543e55f8ca7c35e81bb67aa5cfa79.png)
12+
13+
---
14+
15+
## Full AMP
16+
17+
![](https://i.gyazo.com/bcc2d128064304c355d3c776d41a22a8.png)
18+
19+
---
20+
21+
## GA 対応
22+
23+
![](https://gyazo.com/10b9063d30ff5fb73ec2cc5a3b824333.png)
24+
25+
---
26+
27+
## Git から編集ヒストリの生成
28+
29+
![](ehttps://i.gyazo.com/13034861f0afceacf47ad8d8a09f239a.png)
30+
31+
---
32+
33+
## どんなブログがほしかったか
34+
35+
- Lighthouse で満点出したい
36+
- => どうせ動かないし CDN 上で静的サイト + Full AMP
37+
- 普通の Markdown じゃつまんないから MDX で書きたい
38+
- => コンパイラごと作った(amdx)
39+
- サーバーの運用をしたくない
40+
- => netlify + 買ったまま忘れてたカスタムドメイン(mizchi.dev)
41+
- next.js の最適化に乗りたい
42+
- => `pages/*.tsx` が公開される仕組みを、そのまま採用
43+
44+
---
45+
46+
## next.js の SSG + AMP モードの採用
47+
48+
```tsx
49+
// pages/foo.tsx
50+
export const config = { amp: true };
51+
export default function Foo() {
52+
return <div>foo</div>;
53+
}
54+
```
55+
56+
- `amp: true` で常に amp を生成
57+
- AMP canonical は常に自分自身を指す(のを next.js が勝手にやってくれる)
58+
59+
---
60+
61+
## AMP の plugin を諸々突っ込む
62+
63+
- `amp-social-share` で twitter / facebook / hatena bookmark のシェアに対応
64+
- `amp-analytics` で GoogleAnalytics 対応
65+
- rollup + preact + amp-script で、AMP 上で動的なコンポーネントが作れる。後で何かに使う
66+
67+
---
68+
69+
# AMP 用 Markdown Compiler を作ろう!
70+
71+
---
72+
73+
## AMP 環境の Markdown に求められる仕様
74+
75+
- AMP の仕様を満たす
76+
- `img` => `amp-img` かつ、 amp layout 仕様を満たす固定幅の要素に
77+
- 数式ブロック(`$$ ~ $$`) を amp-mathml に変換
78+
- コードハイライト: **ランタイムで構文解析できない** ので、コードブロックのハイライトを、コンパイル時に済ませておく必要
79+
80+
---
81+
82+
## MDX について
83+
84+
Markdown 中で import 構文が使える仕様
85+
86+
```
87+
# Hello, MDX
88+
89+
import Doc from "./doc";
90+
<Doc />
91+
```
92+
93+
Markdown ドキュメントから、別の Markdown ドキュメントや、React.Component
94+
を展開できる。
95+
シンタックスハイライターなどのエコシステムの都合から、`.mdx`拡張子をそのまま採用したい。
96+
97+
---
98+
99+
## AMDX: Accelarated MDX
100+
101+
- [mizchi/amdx: Accelarated MDX](https://github.com/mizchi/amdx)
102+
- remark ベースで `@mdx-js/mdx` を元に拡張 (中で使ってる babel plugin はそのままなので、構文は互換)
103+
- refract(prismjs parser) で、コンパイル時にコードブロックをトークン化
104+
- +色々 (toc, frontmatter や WebWorker で動くように等)
105+
106+
---
107+
108+
## AMDXG: AMDX による静的サイト生成ツールキット
109+
110+
- amdx-loader: amdx の webpack 用のローダー
111+
- amdxg-components: ブログ用のデフォルトコンポーネント集。使わなくてもいい
112+
- amdxg-cli: ページの雛形や各種メタデータ生成用の CLI
113+
- amdxg-boilerplate: ただのボイラープレート。(注意: まだ安定してない。頻繁に変わる)
114+
115+
---
116+
117+
## 使い方
118+
119+
```
120+
$ npx degit mizchi/mdxx/templates/blog my-blog
121+
$ cd my-blog
122+
$ git init && git commit -m "Init" # 編集履歴生成に git history を使う
123+
$ edit amdxg.config.js # メタデータを編集
124+
125+
# 書く
126+
$ npm i -g amdxg-cli
127+
$ amdxg new:page new-article # docs/new-article.mdx に記事の雛形を生成
128+
$ npx run dev # localhost:3000 でプレビューしながら記事を編集
129+
130+
# build / deploy
131+
132+
$ npx run build # out/ に静的サイトを生成
133+
134+
# 要: netlify account
135+
$ npm i -g netlify-cli
136+
$ netlify deploy -d out --prod
137+
```
138+
139+
---
140+
141+
## 残 TODO
142+
143+
- CSS が雑なのでちゃんとやる
144+
- amxd のプレビュー環境を作る (mdbuf.netlify.com ベースで)
145+
- amdx|amdxg のドキュメントサイトを作る
146+
- vercel 上で任意のバックエンド(CMS)から Incremental SSG する例を作る
147+
148+
(余談だが、 9.4 新機能の Incremental SSG がとても良いですね…)
149+
150+
---
151+
152+
## おわり
153+
154+
Google 検索結果からの遷移が爆速 💪
155+
156+
CSS が苦手なので助けて

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,10 +1556,10 @@ amdx@^0.8.0:
15561556
unist-util-visit "^2.0.2"
15571557
yaml "^1.8.3"
15581558

1559-
amdxg-cli@^0.8.2:
1560-
version "0.8.2"
1561-
resolved "https://registry.yarnpkg.com/amdxg-cli/-/amdxg-cli-0.8.2.tgz#02da2749db9dc69d246c9bf4918386f174de9eb5"
1562-
integrity sha512-wGRBsd6V5FEWp3ZobfvjojpS3bhLIg3Q1961mUHuS0rBKVBvJB8MFAaJ5GgXk8QgDHVBH5wBE6k4oKxHk/EJ2w==
1559+
amdxg-cli@^0.8.4:
1560+
version "0.8.4"
1561+
resolved "https://registry.yarnpkg.com/amdxg-cli/-/amdxg-cli-0.8.4.tgz#49e7b6a967417fae790709323177a63d0c98a71b"
1562+
integrity sha512-uzw1s10F1O/EpyN664v7gBz1vkp8xqQ8q+CgQW3ZjuX87WSmncEWpS+O/Mx7xuB9al1LFnts1huwuPuGifb77A==
15631563
dependencies:
15641564
fs-extra "^9.0.0"
15651565
lodash.sortby "^4.7.0"

0 commit comments

Comments
 (0)