Routifyを使おうとしたけど結局Sveltekitに戻った話


# 前提
この記事は、2024年8月時点での情報で、最新の情報を常に反映しているわけではありません。
また、内容に関する保証はなく、ここで"できない"とされていることが実は可能であることも考えられます。
以上にご留意の上、ご覧ください。

## Routifyとは
https://routify.dev/

こちらのライブラリがRoutifyで、svelteにおいてページ遷移等を行ってくれるルーティングライブラリです。

# 目的
ホームページを作成するため、単なるSSGを行いたいと考えていました。ただし、以下のような条件がありました。
- Svelteを使える
- Sveltekitでの+layout.svelteのようなデザイン統一を行うことができる
- ページはapache等の汎用Webサーバで提供し、SSR(サーバサイドレンダリング)は行わない
- 可能であれば、プレーンなSvelteに対して追加要素が少ない

## なぜRoutifyを選んだのか(Sveltekitではだめだったのか)
Sveltekitでは原則以下のようにファイルを配置する必要があります。
```
.
├── app.d.ts
├── app.html
├── app.pcss
├── lib
│   ├── components
│   │   ├── Confirm.svelte
│   │   └── Notification.svelte
│   └── index.ts
└── routes
    ├── +layout.svelte
    ├── +layout.ts
    ├── +page.svelte
    └── page
        ├── +page.svelte
        └── +page.ts
```
すると、`routes/+page.svelte`が`/`、`routes/page/+page.svelte`が`/page`に対応するわけです。
この状態でsveltekitのadapter-staticを使用してビルドを行うことで静的ページの生成を行うことができます。

しかし、ここで2点の問題点を見つけました。
- 一つページを増やすごとにフォルダを一つ作らなくてはいけない
- .tsファイルでのload関数でしかルータのURLパース機能を利用できない

ただ、デメリットだけでなく、メリットもあります。
- ページごとにコンポーネントやその他のファイルをフォルダ分けできる
- 型の自動生成を的確に行うことができる

また、後述しますがこのようなルーティング構造と従来のファイルベースのルーティング構造を併用することも可能です(スプレッド構文?を使用する)。

このあたりの話は、[sveltekitのディスカッション](https://github.com/sveltejs/kit/discussions/5748)で非常に活発に話し合われていたものなので、詳細は省略します。

結局、Sveltekitのメリットの部分が今回重要ではないと判断したため、SvelteKitではなく、Svelte+Routify+viteという構成を試してみることにしました。

# Routifyを使ってみる
Routifyは、ファイルベースのルーティングを行うライブラリで、ファイルの更新時に自動的にルート設定ファイルを生成するといった仕組みで動作しています。
aタグやhistory.pushStateを使用したページ遷移、urlのパース、(ホバー時の)プレローディングなどについても対応しており、ファイル構造以外Sveltekitとほぼ同様の機能を持っています。
## セットアップ
まず、インストールを行います。
```bash
yarn create vite routify_test --template svelte-ts
cd routify_test
yarn add @roxi/routify@next
```
次にvite.config.tsを編集します(部分抜粋)。
```ts
import routify from '@roxi/routify/vite-plugin'
export default defineConfig({
    plugins: [svelte({ compilerOptions: { hydratable: true } }), routify({
      render: {
        ssg: { enable: false },
        csr: { enable: true }
      }
    })]
})
```
最後に、App.svelteを以下のように編集します。
```svelte
<script lang="ts">
  import { Router, createRouter } from '@roxi/routify'
  import routes from '../.routify/routes.default.js'
  
  export const router = createRouter({ routes })
</script>

<Router {router} />
```

すると、routesフォルダに.svelteファイルを追加すると自動的にルーティングを行うように設定されます。
```bash
# この実行例はlinux上で実行することを想定しています
echo "index page">src/routes/index.svelte
echo "test page">src/routes/test.svelte
yarn dev
# この状態で表示されたlocalhost:xxxxの/や/testにアクセスすると結果が変わることが分かると思います
```
(実際には上のRouterコンポーネントが各ページに入れ替わるような動作をします)

## RoutifyでSSG
以下のようなコマンドでビルドを行います。
```bash
yarn build
```
するとbuild成果物がdistフォルダに作成されます。
しかし、clientフォルダ内には一つ一つ分割されていないindex.htmlを常に返さなければならない形式(俗に言うSPA)のhtmlファイル等が生成されました。

例:
```
dist
└── client
    ├── assets
    │   ├── _..-nuHXFaMl.css
    │   ├── _...404_-CSWWe1Uf.js
    │   ├── index-Cp9fjr8y.js
    │   ├── index-DBGsbjVl.js
    │   ├── index-DqJlrXnh.js
    │   ├── index-xTfApS7Q.css
    │   └── test-BT0jkusZ.js
    ├── index.html
    └── vite.svg
```

きっとSveltekitのそれと同じでtrailingSlashオプションがないとかだろ...と思って探しましたが、見つかりませんでした。
代わりに見つかったのは[ハッシュベースのルーティング](https://v3.ci.routify.dev/docs/guide/faq)(上の例だと、`http://localhost:xxxx/#/test`のような...)ですが、これではSEOなどの観点からもあまり良くないことが想定されます。
(また、flowbite-svleteを使用していたのですが、navbarやbuttonなどのコンポーネントでの画面遷移はhtmlタグを引数で切り替える実装が原因と見られますが、フルリロードがかかってしまうという不具合等もありました。)
そうして私の脱Sveltekitは失敗したのでした。

# Sveltekitに戻って
結局、Sveltekitが一番メンテナンスはされているということもあり戻ることとしましたが、単にSveltekitを使うだけではルーティングに拘った意味がないので、スプレッド構文で取得したパスをもとにルーティングを行うサブルータのようなものを作成しました(が、あまりに安定性とセキュリティに欠けているので今回の掲載は控えさせていただきます)。

# まとめ
今回は、Routifyを用いたファイルベースでのルーティングを行うことを試行しました。
しかし、RCであることもあってか、私のユースケースと合致しないところなどが見受けられました。

このようなSveltekitでのルーティングに関しては一定程度需要があると考えているので、今後はサブルータのライブラリ化やRoutifyへのcontribute、Astro.jsや他言語のSSGフレームワークの採用も含め検討していきたいと考えています。

# 謝辞
この記事に含まれる多くの情報は各フレームワークのコミュニティ(github等)を参考にさせていただきました。
開発に携わられている皆様に敬意を示すとともに、日頃からの貢献に御礼申し上げます。