🐤

Nuxtはどうやってfile-based routingを実現しているか2

2025/01/15に公開

趣旨

勝手に続編を作ってしまいすいません🙇
元の記事ではNuxtのfile-based routingを実現するためのコアな部分(routerに渡すroutesの解決方法と、作成したrouterをvueに適用する処理を行うpluginの実装部分)が分かりやすく解説されており参考になります。元の記事でのfile-based routingを実現するpluginの定義周りの解説に加えて、定義されたpluginをNuxtがどのように処理してブラウザが読み込めるようにしているのかをこの記事ではまとめていきたいと思います。
↓元の記事はこちら
https://zenn.dev/comm_vue_nuxt/articles/62b8dea3a018f9

全体感について


かなり大雑把な全体感ですが、nuxtのコアなinterfaceとしてNuxtとNuxtAppが存在し、ユーザーの設定等を解決して作成されるNuxtと、そのNuxtから作成されるNuxtAppに分かれています。NuxtAppには実際にユーザーのアプリケーション上で動くコードに関わるプロパティが定義されており、NuxtAppがgenerateAppによって処理される事によって動的にコードが生成されます。ユーザーのアプリケーション上で動くコードは当然アプリケーションによって違うものの、最終的にはブラウザに読み込める形として文字列として生成する必要があり、その部分をNuxtAppが吸収する形となっています。

nuxiがloadNuxtを呼び出す

nuxi devでnuxiが実行されると、nuxiはloadNuxtを呼び出してNuxtを作成します。

こちらがnuxiがloadNuxtを実行する部分です。
https://github.com/nuxt/cli/blob/a4cd5dc32aa968bdf56750f7f84277d027a1d15e/src/utils/dev.ts#L178-L195

loadNuxtの返り値はPromise<Nuxt>になっており、このNuxtは後でbuildNuxtに渡すために使用されます。
https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/core/nuxt.ts#L724

installModuleでnuxt.options.pluginsにrouterを追加

loadNuxtを実行するとinstallModuleも実行され、ここでpagesModuleが実行されます。
https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/core/nuxt.ts#L565-L567

pagesModuleではaddPluginが呼び出されており、ここでnuxt.options.pluginにrouterのpluginが追加されます。
https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/pages/module.ts#L485

nuxiがbuildNuxtを呼び出す

nuxiはloadNuxtによって読み込んだNuxtをbuildNuxtに渡します。
https://github.com/nuxt/cli/blob/a4cd5dc32aa968bdf56750f7f84277d027a1d15e/src/utils/dev.ts#L289

generateAppでnuxt.options.pluginsに定義されているpluginsをまとめてexportするコードを生成

buildNuxtではcreateAppでNuxtからNuxtAppを作成し、作成したNuxtAppをgenerateAppに渡します。
https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/core/builder.ts#L15-L19

generateAppではNuxtAppをresolveAppで解決します。
resolveAppでaddPluginによってnuxt.options.pluginsに追加されたpluginがapp.pluginsに追加されます。
https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/core/app.ts#L30-L112

https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/core/app.ts#L198-L219

generateAppで生成されるplugins.client.mjsというコードは、app.plugins定義されているpluginsの中でclient側で読み込む必要のあるpluginを全てimportできるようなコードとなっています。
https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/core/templates.ts#L62-L80

entry.tsでvueAppを作成しpluginを適用する

エントリーポイントの16行目でpluginsを読み込み、83行目でpluginを適用しています。
https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/app/entry.ts#L1-L107

ところで、generateAppで生成したpluginsをまとめてエクスポートするコードのファイル名はplugins.client.mjsだったはずですが、どうして#build/pluginsでimportできるのでしょうか?
#buildの部分はエイリアスで解決できそうですが、.clientの部分は厳しそうです。
その答えはNuxtが追加しているviteのpluginにあり、ここで#build/pluginsという同じmodule specifierでもclientとserverのpluginsを分けて読み込む仕組みが実現されています。
https://github.com/nuxt/nuxt/blob/68ea5c7d85e71b9acc35181666cfcb84d3397e07/packages/nuxt/src/core/plugins/virtual.ts#L8-L27

まとめ

どちらかというと「どうやってfile-based routingを実現しているか」というより、デフォルトで定義されているプラグインやユーザーによって定義されるプラグインをどのようにブラウザに実行させているのかという抽象的な内容になってしまいましたが、元の記事がこの記事を書くきっかけとなったので、このタイトルにさせてもらいます。
また、まだまだ未完成で日本語版も出来ていませんが、Nuxtをstep by stepで実装するchibinuxtの方も実装中なので、興味があればそちらの方もよろしくお願いします。
https://chibinuxt.vercel.app/

Discussion