NextのJSぜんぶ抜く
サイトパフォーマンスを最適化するため、export 時に Next.js のランタイムを全部抜いた。
どういうことか?
このブログは静的書き出しをしているので、ただの静的なドキュメントといえる。だが Next にしろ Nuxt にしろ、書き出したページをブラウザで読み込むと JS アプリケーションとして振る舞うために Hydration 処理が行われる。これをするために JS モジュールの読み込みや Scripting 処理の負荷が発生する。
ピュア HTML を目指して
しかしこのブログはドキュメントでありアプリケーションではないので、ほぼ JS を動かす必要はない。state は不要だし、client-side routing も要らない(先読みに必要かもだが)。なのでピュアに HTML として書き出したい。
Next.js の場合、body の下に <NextScript />
という要素があり、これを消せばまるっと JS が消えるようだ。なので env が production のときだけ消せばいいかと思ったが、調べてみるとちゃんとフル静的化するオプションが experimental で存在するようだ。
Allow disabling runtime JS in production for certain pages (experimental) 🔗 github.com
export const config = {
unstable_runtimeJS: false,
}
このコードを JS 抜きたいページ毎に設定するとフルに静的化される。
動的なパーツは Web Component 化
とはいえ全く script を使わないただの HTML だと困るところも少しある。
以前作った 埋め込みツイートの読み込み部分 は自身の状態に応じてレンダリングを変えたり IntersectionObserver による lazyLoad 処理をしたりと、next のレンダリングとは別の機能を持った要素だ。なので Web Component として独立させた。
あとトップページの記事サムネイルも、画像 onload のタイミングで transition させる制御を入れているので、これも Web Component 化を行った。
Web Component はバニラで書いたり、LitElement、Stencil.js などのライブラリを用いるやり方などがある。いろいろ試してみたところ JSX で書けたり Prop の定義が楽だったりレガシーブラウザ対応がしやすそうという点で最終的には Stencil を使うことにした。
作ったコンポーネントは別レポジトリで管理し、npm に publish して読み込めるようにした。next に依存させないよう、プロジェクトに import するのではなく script タグから読み込むようにしている。
type="module"と nomodule でレガシーとの住み分け
script 読み込みについてはこの動画で詳しく解説されているが、今どきのブラウザはモダンな JS を解釈できるためわざわざ es5 など古いコードにトランスパイルする必要が無い。
なのでレガシー対応するためにはモダン用とレガシー用それぞれにビルドし、ブラウザに応じて必要なほうだけ読み込ませるといい感じになる。script type="module"はモダン向けで、nomodule はレガシー向けになる。
そもそも JS ぜんぶ抜くのなら、最初から非 Node 系静的サイトジェネレータで良かったのでは?
そういう話もある。フロントエンド不要論だ。
Static Site Generators - Top Open Source SSGs | Jamstack 🔗 jamstack.org
非 Node だと Hugo とか Jekyll とかあるのだが、単にテンプレートだけでも JSX じゃないとつらいなと感じる。
追記:11ty もある
Eleventy is a simpler static site generator. 🔗 www.11ty.dev
JSX で書ける SSG ということでは 11ty(Eleventy)もあるよということを教えてもらった。
11ty は利用する Template Language を HTML, Markdown, JavaScript, Liquid, Nunjucks, Handlebars, Mustache, EJS, Haml, Pug
の中からよりどりみどりに選べるようになっている。ただ標準で紹介されている Nunjucks テンプレートの {% %}
記法につい気絶してしまったのでそのへんよく調べてなかった。