Nuxt便利ですね。SSRやSPAも良いですが、静的サイトをちょろっと作るときも、nuxt generate
を使ってコンポーネントベースでさっくり作れたりします。素晴らしい。
ところで。
唐突ですが、DirectoryIndexはご存知でしょうか。ファイル名なしのディレクトリhttps://example.com/foo/bar/
にアクセスした時に、何が表示されるか、という設定です。通常index.html
あたりが設定されていますが、それは絶対的ルールではありません。
サーバにはディレクトリ内のファイルを自動的に一覧してくれる機能とかもあって、まあ公開サーバなんかでは一覧されるとよろしくないことが多いので使わないことが多いですが、テストサーバなどではむしろ一覧を作って欲しい時もあります。そんなとき、GET /
はファイル一覧、GET /index.html
がインデックスファイルの表示、です。
つまり。/
と/index.html
は違うもの、です。
/
と/index.html
は違うもの
Nuxt的にも(Vue Router的にも)両者は違うものです。Nuxtはpagesディレクトリのファイル構成からルーティングを自動生成し、nuxt generate
はルーティングからファイルを生成します。そこのところがすこーしややこしい状況になります。
path | route | file |
---|---|---|
pages/index.vue | / | dist/index.html |
index.vue
からindex.html
が作られる、というのはまあ良いのですが、しかしそのindex.html
のrouteはあくまでも/
であって、/index.html
ではない。ので、/
にアクセスしてDirectoryIndexとしてindex.html
が表示されるならば何の問題もないですが、/index.html
にアクセスしなければ表示できないとなると、routeが一致しないためきちんと動きません。
なんとまあ。
これ、あまりにも自明な問題なのか、あるいは設定一発で簡単に解決できるのか、回避する手立てを見かけないのです。自明な問題の解決策を見抜けないわたしは、泥臭く色々してみました。
ルーティングをいじる
結局のところ、Nuxtのルーティングを少しばかりいじれば良いのです。NuxtのルーターはVue Routerで、Vue Routerにはエイリアスが設定できます。これを用いて、/index.html
を/
のエイリアスにしてしまえば良いはず。
router: {
extendRoutes(routes, resolve) {
routes.push({
path : '/index.html',
alias : '/',
component: resolve(__dirname, 'pages/index.vue')
})
}
},
これで開発サーバで/
と/index.html
にアクセスすると、きちんと両方とも動きます。これで万事解決!!!
では残念ながらありません。誠に遺憾。
試しにnuxt generate
すると…
✖ Nuxt Fatal Error
Error: EISDIR: illegal operation on a directory, open '/Users/aql/tmp/dist/index.html'
はてこれは一体。
ルーティングをベースにgenerateする
繰り返しになりますが、nuxt generate
はルーティングをベースにして静的生成する部分を決めます。そう、上記で加えたエイリアス設定も生成の対象になってしまうのです。
そして今一度思い出すと、ルーティングと生成ファイルの対応はこんな感じでした。
path | route | file |
---|---|---|
pages/index.vue | / | dist/index.html |
pages/foo.vue | /foo | dist/foo/index.html |
/foo
に対して/foo/index.html
が生成される。ということは、/index.html
に対しては/index.html/index.html
が生成される…。これがエラーの秘密のようです。なんたる事だ…。
generate.subFolders
を使う
なんとか逃げる手はないものか。subFoldersという設定があり、上記のように/foo
ディレクトリを作るのではなくfoo.html
を作るように変更ができます。
generate: {
subFolders: false
},
これでディレクトリを作るエラーは回避されますが…生成ファイルとの関係は以下の通り。
path | route | file |
---|---|---|
pages/index.vue | / | dist/index.html |
pages/foo.vue | /foo | dist/foo.html |
- | /index.html | dist/index.html.html |
なんだろう、このイマイチ感。動きますけどね。
generateの時だけルーティングを変更する
仕方がないので、フックを使ってgenerateする時だけルーティングを変更します。無理やり変更したものをさらに無理やり変更する。危険な香りがしますね。
nuxt generateで特定のファイルだけ生成したいという投稿が参考になりました。moduleを作って入れるのでももちろん良いですが、configにも書けるようになっています。とりあえず手間なくこんな感じで。
hooks: {
generate: {
async extendRoutes(routes) {
const filtered = routes.filter(page => page.route != '/index.html')
routes.splice(0, routes.length, ...filtered)
}
}
},
これでnuxt generate
すると…
(゚ー゚)(。_。)ウンウン なんとか大丈夫!
結論
DirectoryIndexの都合で/index.html
にアクセスしなければいけない時は…
-
router.extendRoutes
で/index.html
のエイリアスを追加する -
hooks.generate.extendRoutes
でgenerate時だけ/index.html
のrouteを除去する
おまけ
最終的に、index.html
だけじゃなくて他の*.html
もどうにかしたくなったのでこうなりました。
generate: {
subFolders: false
},
router: {
base: process.env.BASE_DIR || '/',
extendRoutes(routes, resolve) {
const aliases = routes.map(route => ({
path : /\/$/.test(route.path) ? `${route.path}index.html` : `${route.path}.html`,
alias : route.path,
component: route.component
}))
routes.push(...aliases)
}
},
hooks: {
generate: {
async extendRoutes(routes) {
const filtered = routes.filter(page => !/\.html$/.test(page.route))
routes.splice(0, routes.length, ...filtered)
}
}
},
おしまい
もっといい方法ありそうだから、知ってたら教えてね。