プログラミング初心者にオススメ

Vikeとは?ReactでSSGする方法と動的ルートのエラー解決

「Vike」って知っていますか?

Vikeは、ViteをベースにSSRやSSGなどのレンダリング方法を選べるようにするフレームワークです。Reactだけに限らず使えますが、Reactを使う場合は公式の vike-react を組み合わせます。

もともとはViteのプラグイン vite-plugin-ssr として知られていたものが、現在のVikeにつながっています。「Vikeの意味」や「ViteとVikeの違い」を調べている場合は、Viteがビルドツール、Vikeがその上でルーティングやSSR・SSGを扱うためのフレームワーク、と考えると分かりやすいです。

Viteが開発・ビルドの土台で、VikeがルーティングやSSR・SSGを担当します。Reactで使うときは、vike-react を組み合わせてページの描画を構成します。

Vite × ReactでSSGを使いたいと思って探していたところ見つけたのですが、動的ルートのSSGで少しだけハマったので、現行仕様を確認しながらまとめ直しました。

公式ドキュメントは以下です。

Vike公式ドキュメント

VikeでSSGするための基本設定

SSGは、ビルド時にHTMLを生成しておく方式です。Vikeでは、現在は vite.config.js のプラグインにSSG用のオプションを渡すのではなく、ページ側の + ファイルで設定します。

まず、vite.config.js のVikeプラグインは次のように読み込みます。

import vike from 'vike/plugin'

export default {
  plugins: [vike()]
}

SSGの設定は、ページディレクトリの +config.js に書きます。

export default {
  prerender: true
}

そのうえで vike build を実行すると、対象ページのHTMLがビルド時に生成されます。生成先は通常 dist/client/ です。

古い記事やサンプルで見かける vite.config.js 側のSSG設定ではなく、現行の公式案内では +config.js などの + ファイルにVikeの設定を置く 形が基本です。

参考
Vike公式:Pre-rendering
Vike公式:Vite plugin
Vike公式:設定ファイルの移行

ReactアプリでVikeを使う

既存のVikeアプリにReactを追加する場合は、公式の vike-react をインストールします。

npm install vike-react

そして pages/+config.js でReactの設定を拡張します。

import vikeReact from 'vike-react/config'

export default {
  prerender: true,
  extends: [vikeReact]
}

ページのコンポーネントは、例えば次のように +Page.jsx に作成します。

export default function Page() {
  return <h1>Vike + React</h1>
}

参考
Vike公式:vike-react

動的ルートをSSGさせる

ただ、これだけでは /category/@slug のような動的ルートの全ページを自動で生成できません。

動的ルートをSSGするには、ビルドプロセスの中でVike側に生成対象のURL一覧を渡します。

そのために onBeforePrerenderStart() というフックを使います。名前の通り、プリレンダリングの前に実行されるフックです。

このフックが実行されるのは、基本的に vike build でプリレンダリングするときです。開発中の vike dev では実行されないので、動的ページのURL一覧を確認するときはビルド時のログを確認します。

export default '/movie/@movieId'
export { onBeforePrerenderStart }

async function onBeforePrerenderStart() {
  const movies = await Movie.findAll()
  const moviePageURLs = movies.map(movie => '/movie/' + movie.id)
  return moviePageURLs
}

生成したいパスを配列に詰めて返しているだけですね。

実際のコードに当てはめてみた

今回作成していたサイトには /category/@slug という動的ルートがありました。例えば、カテゴリ一覧からURLを作るなら次のようになります。

export default '/category/@slug'
export { onBeforePrerenderStart }

import { categories } from '../../../constants'

async function onBeforePrerenderStart() {
  const categoryPageURLs = categories.map(
    category => '/category/' + category.slug
  )

  // 同じURLを複数回返さない
  return [...new Set(categoryPageURLs)]
}

エラーが発生する

ここで少しハマりました。ドキュメントの通りにファイルを作成したのですが、次のようなエラーが出ました。

Error: [vike][Wrong Usage] URL /category/work provided twice by the onBeforePrerenderStart() hook (/pages/category/+onBeforePrerenderStart.ts). Make sure to provide the URL only once instead.

同じURLが複数出ているので、1回だけ返してほしいという意味です。カテゴリの元データに同じslugが含まれていたり、複数の処理で同じURLを追加していたりすると発生します。

まずは onBeforePrerenderStart() が返す配列を確認して、重複を取り除きます。上の例では Set を使っています。

また、次の点も確認します。

  • +route.js で定義しているパスと、返しているURLの形が一致しているか
  • 同じ動的ルートを別の場所でも定義していないか
  • categories の中に同じ slug が入っていないか

解決方法

今回の構成では、次のように index ディレクトリの中へファイルを移動することで解決しました。

/category/+route.js
/category/+onBeforePrerenderStart.js

↓

/category/index/+route.js
/category/index/+onBeforePrerenderStart.js

このように index というディレクトリを作ってその中に入れる方法です。

これはすべての重複URLエラーに効く万能な解決方法ではありません。まず返却URLの重複とルート定義を確認し、それでもルートの解釈が意図と違う場合にディレクトリ構成を見直す、という順番がよさそうです。

Vikeの公式リポジトリには、動的ルートを含むサンプルもあります。実際のプロジェクトの構成と見比べると、+route.js や +onBeforePrerenderStart.js の配置を考えるときの参考になります。

Vike公式リポジトリのexamples

ビルド前後に確認すること

動的ルートを追加したら、次の順番で確認します。

  1. onBeforePrerenderStart() が返すURLをログに出す
  2. 同じURLがないことを確認する
  3. vike build を実行する
  4. dist/client/ に期待するHTMLが生成されているか確認する
  5. 本番相当の環境で、一覧ページと動的ページのリンクを確認する

SSGはビルド時にHTMLを生成するため、データを追加しただけでは公開済みのHTMLは変わりません。カテゴリなどのデータを更新した場合は、改めてビルドとデプロイが必要です。

最後に

今回はVikeとは何か、ReactアプリでSSGする基本設定、動的ルートを事前生成する方法をまとめました。

以前の記事では vite.config.js にSSGの設定を書いていましたが、現在は pages/+config.js に prerender: true を設定する形が基本です。Vikeや関連パッケージを更新するときは、プロジェクトのバージョンと公式ドキュメントを合わせて確認するのが安全です。

今回も動的ルートで少しハマりました。SSGさせる方法というより、どちらかというとエラー解決記事になっていますね。

VikeはSSRとSSGを同じプロジェクトで扱えるところが便利なので、次はデータ取得やSSRとの使い分けも試してみたいです。