Viteは高速ビルドツールですが、様々なプラグインが開発されており、その中にはVueを静的なHTMLへ出力してくれる(つまり、SSGしてくれる)ものがあったので、試してみました。
プラグインは、 vite-ssg
を使います。
ドキュメント記載の通り、インストールして、セットアップします。
注意すべき点としては、createAppではなく、ViteSSGを使うこと。
// src/main.ts
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
+ // `export const createApp` is required instead of the original `createApp(App).mount('#app')`
+ export const createApp = ViteSSG(
+ // the root component
+ App,
+ // vue-router options
+ { routes },
+ // function to have custom setups
+ ({ app, router, routes, isClient, initialState }) => {
+ // install plugins etc.
+ }
+ )
また、 vue-ssg
のドキュメントで、 vue-router
をインストールしていることから、ルーティングはvue-router
を使います。
createRouter()
して、ルーティング情報を書いても良いのですが、ここでも vite-plugin-pages
というプラグインを使うと便利でした。
これをインストールことで、 src/pages
配下にvueファイルを配置すれば、よしなにルーティングしてくれます。
vite.config.ts
に Pages()
を追加します。
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Pages from 'vite-plugin-pages'
export default defineConfig({
plugins: [
vue(),
+ Pages(),
]
})
TypeScriptの場合はtypesも追加しておきましょう。
// tsconfig.json
{
"compilerOptions": {
+ "types": [
+ "vite/client",
+ "vite-plugin-pages/client",
+ ]
}
あとは、 src/main.ts
に virtual:generated-pages
を使ってよしなにルーティング情報からHTMLを生成してくれます。
// src/main.ts
import { ViteSSG } from 'vite-ssg'
import generatedRoutes from 'virtual:generated-pages'
import App from './App.vue'
+ const routes = generatedRoutes
export const createApp = ViteSSG(
// the root component
App,
// vue-router options
{ routes },
// function to have custom setups
({ app, router, routes, isClient, initialState }) => {
// install plugins etc.
}
)
vite-ssg build
これで問題ないと思いきや・・・
生成したHTMLは、vue-router
を使ってルーティングするため、SSGで静的HTMLを生成しても、直接参照することができません。
たとえば、 localhost:8100
から router-link
を使って、localhost:8100/about
やlocalhost:8100/about/
への遷移は可能ですが、localhost:8100/about
やlocalhost:8100/about/
を直接参照しようとすると「404 not found」が返ってきます。
SSGすると、つくられるのはabout.html
なので、localhost:8100/about
だと「404 not found」が発生してしまうのは当然です。
では、どうすれば良いのか?
apatchやnginx等だと、configファイルなどにURLのパスに対して.html
拡張子付きのファイルを参照するように設定を書くことで回避できます。
しかし、AWS S3などの単純なストレージに対してはそうはいきません。
方法はいくつかありますが、SSGの出力を見直すのが良さそうです。
localhost:8100/about/
は、localhost:8100/about/index.html
のことなので、このようにHTMLを出力するように vite-ssg
側を書き換えます。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Pages from 'vite-plugin-pages'
export default defineConfig({
plugins: [
vue(),
Pages({
+ extendRoute: (route, parent) => {
+ if (!process.env['VITE_SSG']) return route
+
+ if (route.component.match(/\/index\.(vue|js)$/) && route.path !== '/') {
+ return {
+ ...route,
+ path: `${route.path}/index`
+ }
+ }
+ return route
+ }
+ }),
]
})
今回は、vite-plugin-pages
のextendRoute
に書き換え処理を書きましたが、vite-ssg
のssgOptions
でも同様のことができたので、どちらでも良いと思います。(違いなどがあれば教えてください。)
これで about.html
ではなく、 about/index.html
が出力されたことがわかります。
AWS S3だと、localhost:8100/about
もlocalhost:8100/about/
へリダイレクトしてくれるので、これで無事解決です。
さいごに
Vue3でSSGを考えた時、Nuxt3が一番最初に思いつきますが、Nuxt3は現在SSGに未対応なようです。
なので、今現在、Vue3でSSGするならvite-ssg
を使うしかないのですが、思いのほか色々なところで躓いたので、それを記録として残しました。vite-ssg
で躓いている方々への参考になれば幸いです。