2
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?

Snippetのexportで遊ぼう!

Posted at

注意事項

今後動かなくなる可能性があるかもしれません。
自己責任でお願いします。

また、この記事は比較的ニッチな内容を取り扱います。
以下の事前に必要な知識をご存じない方は先にドキュメントを参照ください。

事前に必要なちょっと高度な知識

本題

皆さんはコンポーネントの中で定義したSnippetexportし、親で使用したことがありますか?

また、そのSnippetにリアクティブな値を渡したことがありますか?
この記事ではそんな例を紹介します。

全てのコードは以下のPlaygroundにあります。
https://svelte.dev/playground/66d0481ddba3445b9f0f4e882e4d0aec?version=5.23.0

Message.svelte

<script lang="ts" module>
    // useMessageは他のライブラリなどからimportして使う想定です
	const useMessage = ({ uniqueId }) => {
		const id = $derived(uniqueId());
		return {
			get message() {
				return {
					get props() {
						return {
							'data-message-id': id
						}
					}
				}
			}
		}
	}
	let messageId = $state('id');
	const { message } = useMessage({
		uniqueId: () => messageId
	});
	export { MessageText };
</script>
<script lang="ts">
	import type { Snippet } from 'svelte'
	let { children }: { children: Snippet } = $props();
</script>

<button type="button" onclick={() => (messageId = messageId + ' xxx')}>{messageId}</button>
{#snippet MessageText({ text })}
  <p>id: {message.props['data-message-id']}</p>
  <p>
    {@render text()}
  </p>
{/snippet}
{@render children?.()}

ちょっとずつ説明します。

useMessage

	const useMessage = ({ uniqueId }) => {
		const id = $derived(uniqueId());
		return {
			get message() {
				return {
					get props() {
						return {
							'data-message-id': id
						}
					}
				}
			}
		}
	}

useMessage$stateなどリアクティブな値を関数で受け取り、message.propsdata-message-id: {id}を返す関数です。

意味がわからない例ですが、要するにuseMessageの引数にリアクティブなidを渡したらそれに応じたdata-message-id属性を返すような関数です。

関数の引数の変更を返り値に反映するには関数で渡す必要があります。
ドキュメントにはClassを使えと書いてありますが、自分がClassを気に入っていないのであまり気にせずそうしています。

useMessageにリアクティブな値を渡す

	let messageId = $state('id');
	const { message } = useMessage({
		uniqueId: () => messageId
	});

useMessageに関数で渡しています。

自分が知らなかったポイントとしては<script lang="ts" module>の中でRuneが使えるということです。
全然知らなかった…

export { MessageText };

export { MessageText };

残りのこの部分はテンプレート部分で定義したSnippetexportです。

テンプレート部分

<button type="button" onclick={() => (messageId = messageId + ' xxx')}>{messageId}</button>
{#snippet MessageText({ text })}
  <p>id: {message.props['data-message-id']}</p>
  <p>
    {@render text()}
  </p>
{/snippet}
{@render children?.()}
  • ボタンをクリックすると$stateで管理している値に xxxが追加されていきます
  • MessageTextというSnippetを定義し、引数としてtextという名前のSnippetを受け取り、表示します
  • useMessageの返り値からdata-message-idを表示します
  • デフォルトSnippetchildrenを表示します

MessageTextは直接使用せず、HTMLを絞った状態で親側から使用できるように作ってあります。
DOMを縛れるのは大きいかもしれません。

また、最終的にはこのMessage.svelteを呼ぶ側でMessageTextrenderし、

<Message>
	{@render MessageText({text})}
</Message>

こんな感じで書けばMessage.svelte{@render children?.()}この部分で表示されます。

MessageTextexportしたことで表示する順番や位置が縛られなくなりましたね。

App.svelte

<script lang="ts">
	import Message, { MessageText } from './Message.svelte'
	let message = $state('message')
</script>

<button type="button" onclick={() => message = message + '!'}>
	add message `!`
</button>

<Message>
	{#snippet text()}
		{message}
	{/snippet}
	{@render MessageText({text})}
</Message>

MessageTextMessage.svelteからimportし、<Message>children Snippetとしてrenderしています。

というわけで、Snippetexportをうまく使うと好きにDOMを縛りつつ、しかし柔軟に表示を使う側に委ねるような使い方ができました。

慣れていないと結構わかりにくい気はしますが、ライブラリ制作などで結構役立つかもしれません。
他にも面白い書き方があれば教えてください :thumbsup:

2
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
2
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?