🌺
Svelte5のSnippet引数型付けと動的生成についてメモ
Tabsコンポーネントを作るために色々とSnippetについて調べましたが、結局活用できませんでした。調べた内容で忘れそうなところをメモとして残しておきます。
Snippetの型付けと活用
型付け
Snippet引数の型はタプル[T, U, ...]
として表現する。
Example.svelte
<script context="module" lang="ts">
import type { Snippet } from "svelte";
export type Props = {
foo: Snippet<[number]>,
};
</script>
活用例
表示制御の追加を含めたWrapperコンポーネントのようなものも作成可能。
REPL
コード全体
code
App.svelte
<script>
import Comp from "./Comp.svelte";
</script>
<Comp>
{#snippet body(num)}
<p>Number is {num}!!!</p>
{/snippet}
</Comp>
Comp.svelte
<script context="module" lang="ts">
/*** Export ***/
export type Props = {
body: Snippet<[number]>,
}
/*** import ***/
import type { Snippet } from "svelte";
</script>
<!---------------------------------------->
<script lang="ts">
const { body }: Props = $props();
let count: number = $state(1);
function onclick(): void {
count += 1;
}
</script>
<!---------------------------------------->
<button type="button" {onclick}>increment</button>
{#if count % 3 !== 0}
<div>
{@render body(count)}
</div>
{/if}
<style>
div {
border-style: double;
margin: 0.5rem;
padding: 0.5rem;
}
</style>
簡単な説明
-
Comp.svelte
の機能- 数値インクリメントボタン表示
- 数値が3の倍数の時、結果表示を消す
- 結果表示を二重線で囲む
- 具体的な結果表示内容は呼び出し元が定義可能
Snippetの動的生成と活用
バージョン svelte@5.0.0-next.189 からcreateRawSnippet
関数が追加されプログラムからSnippetを生成できるようになった。
簡単な例
REPL
部分コード
Example.svelte
<script context="module" lang="ts">
import { createRawSnippet } from 'svelte';
export const Greet = createRawSnippet((name: () => string) => {
return {
render: () => `<h1>Hello ${name()}!</h1>`,
setup: (node: Element) => {
$effect(() => {
node.textContent = `Hello ${name()}!`;
});
}
};
});
</script>
Svelte内の型付け
svelte/packages/svelte/types/index.d.ts
index.d.ts
export function createRawSnippet<Params extends unknown[]>(fn: (...params: Getters<Params>) => {
render: () => string;
setup?: (element: Element) => void;
}): Snippet<Params>;
活用例
1つのsvelteファイルで複数の要素を定義・エクスポートできる。
REPL
コード全体
code
App.svelte
<script>
import Comp, { ListItem } from "./Comp.svelte";
</script>
<Comp>
{@render ListItem("Item1")}
{@render ListItem("Item2")}
{@render ListItem("Item3")}
</Comp>
Comp.svelte
<script context="module" lang="ts">
import type { Snippet } from "svelte";
import { createRawSnippet } from 'svelte';
export type Props = {
children: Snippet,
}
export const ListItem = createRawSnippet((text: () => string) => {
const prefixText: () => string = () => "li: " + text();
return {
render: () => `<li>${prefixText()}</li>`,
setup: (li: Element) => {
$effect(() => {
li.textContent = `${prefixText()}`;
li.setAttribute("class", `${text()}`)
});
}
};
});
</script>
<!---------------------------------------->
<script lang="ts">
const { children }: Props = $props();
</script>
<!---------------------------------------->
<h3>List items</h3>
<ul>
{@render children()}
</ul>
簡単な説明
-
Comp.svelte
の機能- コンポーネントとしてリストの枠組みを提供
- 子コンポーネントとしてリストアイテムとしてのSnippetを定義
- 文字列のみ受け付けるリストアイテム
-
createRawSnippet
について- 引数は関数を取る
- 例の
prefixText
のように関数内で値の加工が可能 - 返り値の
render
はHTML要素文字列を指定する - 返り値の
setup
はSnippet引数の値が更新された際の動作を指定する-
mount
,hydrate
時に呼び出される -
setup
が関数の場合、アンマウント時にも呼び出される - 上記REPL例のApp.svelteのような静的用途を想定する場合は
setup
を省略可能
-
Discussion