0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SvelteKit キャッチアップ

Svelteをコンポーネントフレームワークとすると、SvelteKitはアプリケーションフレームワークと言えます。
各ページの書き方はNext.jsのApp Routerに似ていると感じました。

本質的には、SvelteKit が行う仕事は次の3つに集約されます。

ルーティング(Routing) — 受け取ったリクエストにどのルートがマッチするかを判断する
ローディング(Loading) — ルートが必要とするデータを取得する
レンダリング(Rendering) — (サーバー上で) HTML を生成する、または (ブラウザで) DOMを更新する

違う点はapp.htmlがページ全体のtemplateになっており、これに対してSvelteKitがコンポーネントの置き換えを行なっていく、というところだと思います。

Routing

src/route内にある+page.svelteファイルがアプリケーションのページを作成します。
例えば/aboutでアクセスしたいページを作成するためにはsrc/route/+page.svelteを追加すればOKです。

leyout

+layout.svelteを作成することでそのディレクトリ配下にある+page.svelteの共通部分を作成することができます。

動的ルーティング

[]で囲まれたディレクトリを作成することで、動的なパラメーターを持つルートを作成することができます。
例えば、src/route/user/[id]/+page.svelteというファイルを作成することで、/user/1や/user/adminのようなルートが作成されます。

Loading Data

+page.svelteと同じ階層にある+page.server.jsはサーバー側でデータの取得を行い、値をsvelteファイルに受け渡す。

+page.server.js
import { posts } from '../data.js'
import { error } from '@sveltejs/kit'

export function load({ params }) {
	const post = posts.find((post) => post.slug === params.slug);

	if(!post) throw error(404);
	
	return {
		post
	};
}

このload関数が読み取りと受け渡しを行う。(関数名は固定)

+layout.server.js

+layout.svelteと同様に+layout.server.jsも配下のルートがよばれる前にloadが行われる。
+layout.svelteで読み取ることができる。

Setting headers

load関数の中では、レスポンスのヘッダーに情報を追加することができる。

+page.server.js
export function load({ setHeaders }) {
	setHeaders({
		'Content-Type': 'text/plain'
	})
}

Setting cookies

同様にcookieの設定も行うことができます。

+page.server.js
export function load({ cookies }) {
	const visited = cookies.get('visited');

	cookies.set('visited', 'true', { path: '/'});

	return {
		visited: visited === 'true'
	};
}

cookieの設定にはcookies.set(name, value, options)を使用します。
optionsにはこちらのドキュメントのparce,serializeオプションに対応しているようです。

Shared modules

モジュールやコンポーネントは使用するpageファイルの近くに置くのが一般的ですが、複数のページで使用されるものは共通の置き場があると便利です。
SvelteKitではそのディレクトリを、src/libになっています。
インポートの際には、import { name } from '$lib/name.js';のように使用します。

Form

formが一つの場合

データの送信には通常のHTMLと同様にformタグを使用します。

+page.svelte
<form method="POST">
    <label>
        add a todo:
        <input 
            name="description"
            autocomplete="off"
            />
    </label>
</form>

action要素はここでは書かず+page.server.jsactionsとして記述します。

+page.server.js
export const actions = {
	default: async ({ cookies, request }) => {
		const data = await request.formData();
		db.createTodo(cookies.get('userid'), data.get('description'))
	}
}

formが複数ある場合

defaultのみだと2種類以上のformには対応できないので名前をつけて呼び出すactionを振り分けます。

+page.svelte
<form method="POST" action="?/create">
    <label>
        add a todo:
        <input
            name="description"
            autocomplete="off"
        />
    </label>
</form>

<ul class="todos">
    {#each data.todos as todo (todo.id)}
        <li>
            <form method="POST" action="?/delete">
                <input type="hidden" name="id" value={todo.id} />
                <span>{todo.description}</span>
                <button aria-label="Mark as complete" />
            </form>
        </li>
    {/each}
</ul>

action要素の値は?から始まっていますが、これはroute直下にある為です。
todoディレクトリは以下にある場合は、"/todo?/create"のようになります。

+page.server.js
export const actions = {
	create: async ({ cookies, request }) => {
		const data = await request.formData();
		db.createTodo(cookies.get('userid'), data.get('description'));
	},

	delete: async ({ cookies, request }) => {
		const data = await request.formData();
		db.deleteTodo(cookies.get('userid'), data.get('id'));
	}
}; 

Validation

必須項目などはHTML部分で防ぐことができます。
inputタグ内にrequiredを追加します。

+page.svelte
<script>
	export let data;
+	export let form;
</script>

<div class="centered">
	<h1>todos</h1>

+	{#if form?.error}
+		<p class="error">{form.error}</p>
+	{/if}

	<form method="POST" action="?/create">
		<label>
			add a todo:
			<input
				name="description"
+				value={form?.description ?? ''}
				autocomplete="off"
+				required
			/>
		</label>
	</form>
+page.server.js
+ import { fail } from '@sveltejs/kit';

export const actions = {
	create: async ({ cookies, request }) => {
		const data = await request.formData();
+		try {
			db.createTodo(cookies.get('userid'), data.get('description'));
+		} catch (error) {
+			return fail(422, {
+				description: data.get('description'),
+				error: error.message
+			})
+		}
	},

	delete: async ({ cookies, request }) => {
        // ...
    }
};

failでラップした値はformとして+page.svelteに返すことができます。

enhance

import { enhance } from '$app/forms';をしてuse:enhanceをFomrタグに追加することでページをリロードするのではなくなったため、トランジションなどが効くようになります。

use:enhanceは独自にカスタマイズを行うことができます。送信中の表示(sending...など)はこれを使って操作できます。

<form
    method="POST"
    action="?/create"
    use:enhance={() => {
        creating = true;

        return async ({ update }) => {
            await update();
            creating = false;
        };
    }}
>

API Route

ディレクトリ内に+server.jsファイルを追加し、そこでHTTP メソッド GET、PUT、POST、PATCH、DELETE に対応する関数をエクスポートすることで、 API ルート(API routes) を作成することができます。

src/route/roll/+server.js
import { json } from '@sveltejs/kit';

export function GET() {
	const number = Math.floor(Math.random() * 6) + 1;

	return json(number);
}

呼び出す側ではfetch('/roll');のように、ディレクトリ名をURL部分に設定する。

Store

SvelteKit では、3つの読み取り専用 store (page、navigating、updated )を $app/stores から使用できます。

pageで取得できる情報

  • url
    現在のページの URL
  • params
    現在のページのパラメータ
  • route
    現在のルート(route)を表す id プロパティを持つオブジェクト
  • status
    現在のページの HTTP ステータスコード
  • error
    現在のページのエラーオブジェクト
  • data
    現在のページの data。全ての load 関数からの戻り値が足されたもの
  • form
    form action から返されるデータ

他の store と同様、コンポーネントでは $ シンボルを先頭に付けることでその値を参照することができます。例えば、現在のパス名は $page.url.pathname でアクセスできます:

navigatingで取得できる情報

リンクをクリックしたり、ブラウザの '戻る/進む'、またはプログラムで goto を呼んだときに値を持つ。
navigating の値は以下のプロパティを持つオブジェクトになります:

  • from/to
    params、route、url プロパティを持つオブジェクト
  • type
    ナビゲーションのタイプ(link、popstate、goto)

updatedで取得できる情報

updated store は true または false を持ちます。これは最初にページを開いてからそれ以降にアプリの新バージョンがデプロイされたかどうかを表しています。動作させるには、svelte.config.js で kit.version.pollInterval を指定する必要があります。

バージョンの変更はプロダクションでのみ発生し、開発時には発生しません。そのため、このチュートリアルでは $updated は常に false となります。

pollInterval とは関係なく、updated.check() を呼び出すと手動で新バージョンがデプロイされたかチェックできます。

error

SveltKitには想定される(expected)エラーと、予期せぬ(unexpected)エラーの二種類があります。

  • expected error
    想定されているエラーは500以外のエラーのことです。
    エラーの原因がわかっているものなので適切なエラーコードと共にthrowします。

    +page.server.js
      import { error } from '@sveltejs/kit';
    
      export function load() {
      	throw error(420, 'Enhance your calm');
      }
    
  • unexpected error
    予期せぬエラーの場合は、アプリのバグであると考えられます。予期せぬエラーがスローされた場合、そのメッセージとスタックトレースがコンソールにログ出力されます。
    画面にはエラーの内容ではなく、'Internal Error' メッセージと 500 ステータスコードが表示されます。

+error.svelte

src/routes/+error.svelte コンポーネントを作成することで、エラーページをカスタマイズすることができます。

エラーが起きた階層から一番近い+error.svelteページが表示されます。

最上位(root)のレイアウトでデータをロードしているときや、エラーページのレンダリング中にエラーが発生した場合、SvelteKit は静的なエラーページにフォールバックします。

src/error.htmlファイルを作成することで、フォールバックエラーページをカスタマイズすることができます。

src/error.html
<h1>Game over</h1>
<p>Code %sveltekit.status%</p>
<p>%sveltekit.error.message%</p>

このファイルは以下の項目を含めることができます。
%sveltekit.status% — HTTP ステータスコード
%sveltekit.error.message% — エラーメッセージ

redirect

throwの仕組みを応用して、別ページへのリダイレクトを行うことができます。
throw redirect(dode,route) は、load 関数、form actions、API ルート、そして後の章で説明する handle hook の内側で使うことができます。

よく使用されるステータスコードはこちらです。

  • 303
    form actions で、送信に成功したあと続いて使用されます
  • 307
    一時的なリダイレクトに使用されます
  • 308
    恒久的なリダイレクトに使用されます
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?