SvelteKit デモアプリからルーティングについて解説する

# はじめに

`svelte`では[svelte-routing](https://github.com/EmilTholin/svelte-routing)を用いてルーティングを実装したことがありましたが、`SvelteKit`では標準でルーティングが用意されています。
今回は公式デモを構築しつつ、ルーティングについてまとめていく。


# SvelteKitデモアプリの構築

1. `SvelteKit`構築

    ```bash
    $ npm create svelte@latest
    Need to install the following packages:
      create-svelte@2.0.0-next.198
    Ok to proceed? (y) y
    
    create-svelte version 2.0.0-next.198
    
    Welcome to SvelteKit!
    
    This is release candidate software; expect bugs and missing features.
    
    Problems? Open an issue on https://github.com/sveltejs/kit/issues if none exists already.
    
    ✔ Where should we create your project?
      (leave blank to use current directory) … 
    ✔ Which Svelte app template? › SvelteKit demo app
    ✔ Add type checking with TypeScript? › Yes, using TypeScript syntax
    ✔ Add ESLint for code linting? … No / Yes ›  No
    ✔ Add Prettier for code formatting? … No / Yes ›  No
    ✔ Add Playwright for browser testing? … No / Yes ›  No
    ✔ Add Vitest for unit testing? … No / Yes ›  No
    
    $ npm install
    ```

1. localhost:8080で開くように調整する

    ```javascript
    // vite.config.js

    import { sveltekit } from '@sveltejs/kit/vite';
    
    /** @type {import('vite').UserConfig} */
    const config = {
    	darkMode: 'class',
    	plugins: [sveltekit()],
    	server: {          // 追加
    		host: true,    // 追加
    		port: 8080     // 追加
    	}                  // 追加
    };
    
    export default config;
    ```

    `8080`以外のポートを開けたい場合は`port`の部分を調整する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/86a7ad9e-5605-629f-5d90-a64b1f15c08d.png)


# `routes`ディレクトリについて

## デモから読み取る

このデモアプリケーションは↓のような構造で構成されています。

簡単に説明すると、`routes`以下のディレクトリがそのままルーティングされます。このあたりは`nuxt.js`とかとも似てますね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/19405778-9a98-8895-2d73-5fe9049223ae.png)

どのようにページを表示しているのか見ていきます。

### +page.svelte

そのルーティングのアプリのページを表しています。

ルートパス( `/` )では、`routes/+page.svelte`
アバウトパス( `/about` )では、`routes/about/+page.svelte`

がそれに該当します。

`routes`以下に作ったディレクトリ名に**＋ページ**するイメージですね！

### +page.ts

`+page.svelte`が呼ばれる前に実行されます。

```typescript
import { error } from '@sveltejs/kit';
 
/** @type {import('./$types').PageLoad} */
export function load() {
    return {
        profile: [{ hoge: 'hoge', fuga: 'fuga' }]
    };
}
```

↑のようにデータを返すことで、`+page.svelte`で使うことができます。

```vue
<script>
  /** @type {import('./$types').PageData} */
  export let data;

  console.log(data.profile); // [{ hoge: 'hoge', fuga: 'fuga' }]
</script>
```

### +layout.svelte

全てのページで表示されるページです。
`+page.svelte`等の表示場所は`<slot></slot>`を使うことで表示させることができます。

ヘッダーやフッター、cssの読み込みなどを行うのに便利そうです。

例)
```vue
// +layout.svelte

<script>
	import Header from './Header.svelte';
	import Footer from './Footer.svelte';
	import './styles.css';
</script>

<div class="app">
	<Header />
    <slot><slot/>  // +page.svelte
	<Footer />
</div>
```

### +layout.ts

`+layout.svelte`が呼ばれる前に実行されます。
使い方は`+page.ts`と同じです。

## デモで使われていないもの

### [パラメータ]

`/user/1`の`1`や、`store/tokyo`の`tokyo`の値をパラメータとするようなルーティングを実装するときは

`routes/user/[id]`や、`routes/store/[place]`のようにディレクトリを作成し、その下に`+page.svelte`を作成する。

例) `about/:id`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/3a49caf2-2065-5abe-f296-469d1f17db0b.png)

`+page.ts`では、次のようにしてパラメータを取得することができる

```typescript
// +page.ts
import { error } from '@sveltejs/kit';
 
/** @type {import('./$types').PageLoad} */
export function load({ params }) {
  if (params.id === '1') {
    return {
      title: 'Hello 1号！',
      content: 'Welcome to our blog. Lorem ipsum dolor sit amet...'
    };
  }
  
  // id: 1以外は404エラー等...
  throw error(404, 'Not found');
}
```

そして先ほどと同じやり方で`+page.svelte`に値を渡し、表示させることができる

```vue
// +page.svelte

<script>
  /** @type {import('./$types').PageData} */
  export let data;
</script>

<h1>{data.title}</h1>
<div>{@html data.content}</div>
```

localhost:8080/about/1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/ce368964-7426-68b4-d944-ffd007cfb92a.png)



### server.ts

データベースから値を取得したり、環境変数にアクセスするような、
サーバーサイドで実行したい処理を実行する場合には`server.ts`を使う

`+page.ts`ならば、`+page.server.ts`
`+layout.ts`ならば`+layout.server.ts`を使う。

```typescript
// +page.server.ts
import { error } from '@sveltejs/kit';
import { getData } from './common'
 
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
  const post = await getData(params);
 
  if (post) {
    return post;
  }
 
  throw error(404, 'Not found');
}
```

### +error.svelte

ページロード中にエラーが発生した場合に表示させるページ。
`$app/store`というモジュールに`page`というのものが用意されており、
組み合わせて使うことで、良い感じのエラーページを表示させることができる。

```vue
<script>
  import { page } from '$app/stores';
</script>

<h1>{$page.status}</h1>
<h1>{$page.error.message}</h1>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/006bf54a-0414-d5c0-489f-4ef4e6b85111.png)


### $types

これまでの例で`/** @type {import('./$types').PageData} */`が度々登場してましたが、
`+page.svelte`や`+layout.svelte`で`export let data;`の前の行にこのアノテーションを付けると、その型は`load`関数の戻り値ということをTypeScriptに伝えることができます。

TypeScriptを使う人は正しく型をつけるためにも積極的につけていきましょう


# 次回

次回は`goto`等、`$app/navigation`について解説していきます。