前提
- デザイナーはTypeScriptに詳しくない
- リリース後の運用(UIの変更など)をデザイナーが行う
+page.svelte
SvelteKitではroutesディレクトリ配下に+page.svelteを配置するとページを作成することができます。
また、+page.svelteはただのSvelteコンポーネントなので他のファイルからimportすることができます。
この記事はそれを利用してみる内容となっています。
特定のページでしか使用しないコンポーネント
今回例としてLPのようなページを考えます。このLPは他のページと完全に独立しています。
また、lp/_components/に配置したPosts.svelteやlp/+page.server.tsはlp/+page.svelteでしか使用しないものとします。
このような状況ではlpディレクトリの近くにPosts.svelteを置くのがいいと考えています。
今後共通で使用することがないとわかっている、かつ他で使って欲しくないコンポーネントを離れた別の場所に置きたくないためです。
この状況下ではPostsコンポーネントのpropsに関しても
+page.server.ts -> +page.svelte -> Posts.svelte
と流れてくる以外ないので、Posts.svelteコンポーネントのpropsの型も+page.svelteから持ってきてしまってよいでしょう。
つまりこうです。
<script lang="ts">
import type { ComponentProps } from 'svelte';
import type Page from '../+page.svelte';
type PageDataPosts = ComponentProps<typeof Page>['data']['posts'];
let { posts }: { posts: PageDataPosts } = $props();
</script>
+page.server.tsは以下です。
declare const __brand: unique symbol;
type BrandedType<T, B> = T & { [__brand]: B };
type PostId = BrandedType<number, 'id'>;
type UserId = BrandedType<number, 'userId'>;
type Post = {
id: PostId;
title: string;
body: string;
userId: UserId;
};
type PageServerData = {
posts: Post[];
};
export const load = async (): Promise<PageServerData> => {
const posts = await fetch('https://jsonplaceholder.typicode.com/posts/').then(
(response) => response.json()
);
return {
posts: posts.slice(0, 10),
};
};
Post型のidやuserIdにはBrandedTypeを使用しておりかつその型をexportしていないので、
今後例えばPosts.svelteを拡張したXXXPosts.svelteを「TypeScriptに詳しくないデザイナー」が作成した時に既存のPosts.svelteのpropsを見に行くように誘導できるので少し安心です。
また、Posts.svelteのpropsに+page.svelteの型を使用することによって、このコンポーネントが+page.svelteのデータを表示することを明示できました。
まとめ
このような使い方をする機会はあまりないと思いますが、+page.svelteの型の再利用や+page.svelteをStorybookで表示するなどのテクニックとして覚えておいて損はありません。
個人的には+page.svelteや+layout.svelteに色々書きすぎるとStorybookでの表示やテストの時にしんどくなると思っており、Page.svelteやLayout.svelteコンポーネントを作って+page.svelteはそのページのPage.svelteを読み込んでいるだけ、+layout.svelteは極力使用しない という選択肢もありだと思っています。
この記事はこういうこともできるよ というアイデア、知識として読んでもらえればと思います。
最後まで読んでくださりありがとうございました!
みなさんのちょっと変わったSvelteKit、Svelteの使い方の記事も読みたいのでぜひ書いてください🙏
