注意事項
今後動かなくなる可能性があるかもしれません。
自己責任でお願いします。
また、この記事は比較的ニッチな内容を取り扱います。
以下の事前に必要な知識をご存じない方は先にドキュメントを参照ください。
事前に必要なちょっと高度な知識
本題
皆さんはコンポーネントの中で定義した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を縛りつつ、しかし柔軟に表示を使う側に委ねるような使い方ができました。
慣れていないと結構わかりにくい気はしますが、ライブラリ制作などで結構役立つかもしれません。
他にも面白い書き方があれば教えてください