Viteとesbuildを組み込みReact製SSGを再構築 - minista v2
ReactのJSXで書けるスタティックサイトジェネレーター(以下SSG)ministaのv2をリリースしました!webpackをやめてViteとesbuildを組み込みゼロから再設計。1番の悩みだったパフォーマンスを大幅に改善することができました。
開発サーバーは規模に関係なく 0.6 秒ほどで立ち上がり、本番ビルドの時間も半分近く短縮しています。Next.jsと親和性のある機能改善も行い併用が楽になったかと。今回はドキュメントサイトも作成しましたので、使い方はそちらに任せ、ここでは開発の話のみ書きます。
webpackからのVite
webpackの代替としていくつか試してみてViteを選びました。Snowpackは動作と先行きが不安で除外。Parcelはnpm installすると180MBもあり除外。そのほか小さいライブラリから組み上げるのは辛そうで、一番バランスよく使えそうなNo-BundlerツールがViteでした。
ただ、No-BundlerツールはどれもSSGに欠かせないHTML生成まではサポートしていません。開発サーバーとアセットビルドはなんとかなるけど、SSGは自前で作る必要がありました。
SSGはesbuildに任せた
シンプルに考えると .tsx
ファイルを renderToStaticMarkup()
でHTML化して fs.writefile()
するCLIさえ作れれば良いんじゃないかと。esbuildで100ページ生成して遅かったらRust言語を試す予定でしたが、速かった(1.27秒)のでそのまま採用しました。
開発中はSPAでビルドはSSG
開発と本番の差異はなるべく少なくしたいところでしたが、快適さを優先して開発中をSPAにしました。ViteのSPAだとHMRが使えるため、文字修正程度でフルリロードせずに済みます。インブラウザデザインしているとフラッシュが鬱陶しかったので楽になりました。
Viteのindex.htmlを消した
ministaはNext.jsの体験をモデルにしているので、Viteの剥き出しなindex.htmlが厄介でした。設定で任意の場所に移せないんですよね。リポジトリを漁っていたらvitepressだけindex.htmlが無かったので、参考にして独自HTMLを返すViteプラグインを内蔵させました。
getStaticDataの実装
Next.jsの getStaticProps
と getStaticPaths
が使えたらCMSにデータ管理を分離できて楽だろうなーと以前から考えていて、丸替えするついでに実装しました。Astroが1つの関数にまとめていたので、作りはそちらを参考にしましたが。
全体を包むRootの実装
Next.jsは _app.tsx
で getStaticProps
使えないんですよね。グローバルなmetaデータ流し込みたいのに。せっかくなのでministaではラッパーコンポーネントで使えるようにしました。命名はAppだとSSGっぽくないのでRootで。
MDX 2を統合
Remixに最初からMDXが入っていたので見習って統合。これを実装しておいたおかげでministaのドキュメントサイトはMarkdownのみで書けて楽でした。
SVG Sprite Iconsの再実装
Viteでもプラグインでなんとかなるだろうと後回しにしてたところ、なんともならず独自実装となりました。SVG周りのnpmライブラリは肥大化や脆弱性の問題があるものが多く、最終的にsvgstoreを選びましたが、こちらはtypesが無いのが辛かったです。
CMSの画像ダウンロード
CMSの画像データ転送量の削減・CMSのスリープ・ローカルCMS連携を想定し、画像ダウンロード機能を試験的に実装しました。枚数が多いとビルドが遅くなるので、ブログ運用は画像APIを使う方が良いと思いますが、少量の場合やCMSを切断したい場合は有用です。
細かい改善点
テンプレートの処理が自由になったことで render()
をページに書く必要がなくなったり、コンポーネントでCSSをimportできるようになったりと、面倒だった部分も改善されました。
Gatsbyの代わりになるかも
SaaSのテンプレ生成業務用として作り始めたministaですが、CMS連携が可能になったことで既存のSSGを置き換えられるかもしれません。MDXが入っているので、HugoやHexoで昔作っていたサイトを載せ替えるのは容易でしょう。
それよりも直近の悩みはGatsbyで、ビルドが遅く辛いです。Gatsby Cloudを有料契約してインクリメンタルビルドしていましたが、それでも時間がかかります。Reactを多用しない場合はministaで作るという選択肢もありかもしれません。
Discussion