SvelteでatomicなHTMLコンポーネントを宣言する
やりたいこと
各HTMLタグのみを返す最小単位のuiコンポーネントを作りたい。
Reactで書くと以下のようなもの。
import type { ButtonHTMLAttributes } from "react";
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
export function Button({ ...props }: ButtonProps) {
return (
<button
className={`${props.className} h-10 w-36 rounded-full`}
{...props}
/>
);
}
HTMLタグにデフォルトで備わっているattributes(属性)をpropsとして受け取り、最低限のスタイルだけ当てている。
(上のコンポーネントはtailwindcssでスタイルを当てています。)
結論
以下パッケージをインストール
以下のように指定するとpropsとイベントを親から受け取れる。
<script lang="ts">
import type { HTMLButtonAttributes } from "svelte/elements";
type $$Props = HTMLButtonAttributes;
</script>
<button
class={`${$$props.class} h-10 w-36 rounded-full`}
on:*
{...$$props}
>
<slot />
</button>
解説
attributesを受け渡し
$$props
を使用するとexport let
で宣言していないpropsを受け取れる。
<button {...$$props} />
このままだと<button>
に存在しない属性も渡せてしまうので、$$Props
の型を上書きしておく。
type $$Props = HTMLButtonAttributes;
event forwarding
$$props
だけだと<Button on:click={...} />
などイベントが実行されなくなってしまう。
eventを親から指定できるようにする方法はsvelte公式ドキュメントの以下に記載されている。
clickイベントを親から指定したい場合は以下のようにすればいい。
<button on:click />
全てのイベントを記述するのはめんどくさいのでうまいこと指定したい。
svelte公式では v5で実装予定。
svelte v3 ~ v4の場合は以下のサードパーティライブラリを使用
インストール手順はREADMEにありますが、一応紹介。
pnpm add -D svelte-preprocess-delegate-events
プロジェクトルートにsvelte-jsx.d.ts
を作成して以下内容をペースト
declare namespace svelteHTML {
type HTMLProps<Property extends string, Override> =
Omit<
Omit<import('svelte/elements').SvelteHTMLElements[Property], keyof EventsWithColon<Omit<svelte.JSX.IntrinsicElements[Property & string], svelte.JSX.AttributeNames>>> & EventsWithColon<Omit<svelte.JSX.IntrinsicElements[Property & string], svelte.JSX.AttributeNames>>,
keyof Override
> & Override & (Record<'on:*', (event: Event & { currentTarget: EventTarget & EventTarget }) => any | never> | object);
}
tsconfig.json
に以下を追加
{
"include": ["./src/**/*", "./svelte-jsx.d.ts"]
}
これでon:*
とすることで全てのイベントを親から指定できます。
注意
svelteのコンパイル性能を最大限引き出すには$$props
を使用せずに必要なpropsのみexportする方がいいみたいです。
$$propsは、export
で宣言されていないものも含めて、コンポーネントに渡されるすべてのプロパティ (props)を参照します。
$$props
を使用すると、特定のプロパティを参照するときよりもパフォーマンスが低下します。なぜなら、どのプロパティを変更した場合でも、Svelte は$$props
のすべての使用を再チェックするからです。
しかし、例えばコンパイル時にどのようなプロパティがコンポーネントに渡されるかわからない場合などには便利です。
svelteは学習中ですので、間違いや他にいい方法があればご教授下さい。
Discussion