注意事項
今後動かなくなる可能性があるかもしれません。
自己責任でお願いします。
また、この記事は比較的ニッチな内容を取り扱います。
以下の事前に必要な知識をご存じない方は先にドキュメントを参照ください。
事前に必要なちょっと高度な知識
本題
皆さんはコンポーネントの中で定義したSnippetをexportし、親で使用したことがありますか?
また、その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.propsにdata-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 };
残りのこの部分はテンプレート部分で定義したSnippetのexportです。
テンプレート部分
<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を表示します - デフォルト
Snippetのchildrenを表示します
MessageTextは直接使用せず、HTMLを絞った状態で親側から使用できるように作ってあります。
DOMを縛れるのは大きいかもしれません。
また、最終的にはこのMessage.svelteを呼ぶ側でMessageTextをrenderし、
<Message>
{@render MessageText({text})}
</Message>
こんな感じで書けばMessage.svelteの{@render children?.()}この部分で表示されます。
MessageTextをexportしたことで表示する順番や位置が縛られなくなりましたね。
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>
MessageTextをMessage.svelteからimportし、<Message>のchildren Snippetとしてrenderしています。
というわけで、Snippetのexportをうまく使うと好きにDOMを縛りつつ、しかし柔軟に表示を使う側に委ねるような使い方ができました。
慣れていないと結構わかりにくい気はしますが、ライブラリ制作などで結構役立つかもしれません。
他にも面白い書き方があれば教えてください ![]()