💡

【SvelteKit】「ローカルなコンポーネント」というアイデア

2025/03/24に公開

まとめ

  • コンポーネントの記述方法は、スコープの広さが異なるいくつかの方法が考えられる
    • snippet < ローカルなコンポーネント < 共通ライブラリとしてのコンポーネント
    • 必要最低限のスコープを有するコンポーネントを採用すべき
  • ディレクトリ構造を有するコンポーネントの場合、+page.svelte のように、+component.svelte と命名するのも一考(諸説あり)


背景

当団(Orchestra Canvas Tokyo)では、ホームページやブログをSvelteKitを用いて開発しています。

小規模ながらサイトの開発・保守を続ける中で、SvelteKitにおいて「ローカルなコンポーネント」という概念が有用なのではないか?という私見が生まれました。
今回はそれについてまとめていきます。

私が調べた限りでは類似の意見を見つけきれなかったのですが、もし心あたりがある方がいらっしゃいましたら、教えていただけると嬉しいです!


できるだけローカルに記述する

コンポーネントはできるだけ「ローカルに記述」するのが好ましいと考えます。
ちょうど、一般のプログラミングでスコープをできるだけ狭くするようなイメージです。

具体的には、規模が小さい順に次の選択肢を採用するべきだと考えています。

  1. スニペット
  2. ローカルなコンポーネント
  3. 共通ライブラリとしてのコンポーネント

順に述べていきます。

スニペット

Svelte5の新機能です。
ちょうど、一般のプログラミングで 共通処理を関数として再利用 するように記述することができます。

https://svelte.jp/docs/svelte/snippet

+layout.svelte
<script>
  import logoSource from './logo.svg'
  let { children } = $props();
</script>

{#snippet logo()}
  <a href="/">
    <img src={logoSource} alt="Orchestra Canvas Tokyo" width=300 height=100>
  </a>
{/snippet}

<header>
  {@render logo()}
</header>

{@render children()}

<footer>
  {@render logo()}
</footer>

ローカルなコンポーネント

ちょうど、一般のプログラミングで 長めの共通処理を、別ファイルとして切り出す ように管理することができます。

プロジェクト構成
/src/
└ routes/
 ├ +layout.svelte
 ├ Logo.svelte
 ├ logo-large.svg
 └ logo-small.svg
Logo.svelte
<script>
  import logoLarge from './logo-large.svg'
  import logoSmall from './logo-small.svg'
</script>

<a href="/">
  <picture>
    <srcset srcset={logoLarge} media="(min-width: 600px)">
    <img src={logoSmall} alt="Orchestra Canvas Tokyo">
  </picture>
</a>

<style>
  img {
    /* 省略 */
  }
</style>
+layout.svelte
<script>
  import Logo from './Logo.svelte';
  let { children } = $props();
</script>

<header>
  <Logo />
</header>

{@render children()}

<footer>
  <Logo />
</footer>

共通ライブラリとしてのコンポーネント

ちょうど、一般のプログラミングで 離れた複数ファイルの共通処理を、ライブラリとして切り出す ように管理することができます。

一般のプログラミングと同様に、「本当に同じことを意味する処理なのか」を検討する必要がありますが、汎用性が高く強力な一手です。
強力ではある分、依存関係が不明瞭になり、コードの見通しの悪さの原因となりえることに留意する必要があります。

プロジェクト構成
/src/
├ lib/
│└ Logo
│ ├ Logo.svelte  ※ 任意のファイル名を指定可能
│ ├ logo-large.svg
│ └ logo-small.svg
└ routes/
 └ +layout.svelte
Logo.svelte
<script>
  import logoLarge from './logo-large.svg'
  import logoSmall from './logo-small.svg'
</script>

<a href="/">
  <picture>
    <srcset srcset={logoLarge} media="(min-width: 600px)">
    <img src={logoSmall} alt="Orchestra Canvas Tokyo">
  </picture>
</a>

<style>
  img {
    /* 省略 */
  }
</style>
+layout.svelte
<script>
  import Logo from '$lib/Logo/Logo.svelte';
  let { children } = $props();
</script>

<header>
  <Logo />
</header>

{@render children()}

<footer>
  <Logo />
</footer>
ファイルの命名案:+component.svelte

団内でも意見が分かれた提案です。

先の例にも上げましたが、ディレクトリ構造をもつコンポーネントについて、コンポーネントに +component.svelte と命名するのは いかがでしょうか
ちょうど、ルーティングにおいて +page.svelte+layout.svelte を作成する形式をまねたものです。

以前の記事で述べた「ローカルなアセット」の考え方を、コンポーネントに対するアセットにも応用することができます。
アセットを用いない場合は、シンプルさを優先してディレクトリ構造を採用しないのも手です。

これはオリジナルの管理ですので、将来的に問題が生じる可能性を否定できません

また、開発者の混乱を招く可能性があります
.gitapplyというファイルがあったらどんな仕様か調べて回るよね?という団員の意見があり、もっともなだなぁと感じています。

メリットとしては、routes/ 配下と統一感のある管理ができること、+page.svelteと同様にエクスポート上で見通しがよくなることが挙げられます。

先の例について、この方式を採用すると次のようになります。

プロジェクト構成
/src/
├ lib/
│└ Logo
│ ├ +component.svelte
│ ├ logo-large.svg
│ └ logo-small.svg
└ routes/
 └ +layout.svelte
+layout.svelte
<script>
  import Logo from '$lib/Logo/+component.svelte';
  let { children } = $props();
</script>

<header>
  <Logo />
</header>

{@render children()}

<footer>
  <Logo />
</footer>

おわりに

最後までお読みいただきありがとうございます!

グローバル変数、ローカル変数といった既知の概念における「グローバル」「ローカル」の意味するところを分解し、「アセット」や「コンポーネント」に適用する――

このような応用をして遊んでみると、たま~に思いがけずキレイな知見に辿り着くことがあります。
かけがえのない、楽しい瞬間です。

情報の海の上を泳ぎ周る日々ですが、たまにはぷかぷかと漂ってみるのも悪くないものです!


次回演奏会のご案内

Orchestra Canvas Tokyoは、都内を中心に活動するアマチュアオーケストラです。

日々の癒しに、新たなひらめきのきっかけに——
オーケストラの演奏会はいかがでしょうか?

初めての方も大歓迎!
ご来場をお待ちしています。

Orchestra Canvas Tokyo
第14回定期演奏会

2025年7月12日(土)
練馬区立練馬文化センター 大ホール
シューマン / 交響曲第2番 ほか

詳細はチケット販売ページにて

GitHubで編集を提案
OCTテックブログ

Discussion