プログラミング/svelte/svelte5への移行 の履歴(No.1)
更新新しいバージョンではかなりいろいろ変わるらしい†
Svelte 5†
- export let で定義していたプロパティは $props() で定義するようになった
- 省略可能なものはタイプ名を ? 付きにする
- bind で使うものはさらに $bindable() とする
- <slot /> ではなく Snippet を {@render } するようになった
- on:click の形で Event Forwarding することはできなくなったので、正攻法で oninput?: FormEventHandler<HTMLInputElement> のようなプロパティを定義することになる
src/lib/components/Dialog.svelte
<script lang="ts"> import type { Snippet } from 'svelte'; let { title, children }: { title: string; children: Snippet } = $props(); </script> <div class="dialog relative flex min-h-screen flex-col items-center justify-center"> <div class="m-auto w-full rounded-md p-6 shadow-2xl lg:max-w-lg"> <h1 class="text-center text-3xl font-semibold text-primary">{title}</h1> {@render children()} </div> </div>
src/lib/components/Form.svelte
<script lang="ts"> import type { Snippet } from 'svelte'; let { message = '', enhance = () => { return {}; }, props = {}, children, }: { message?: string; enhance?: (el: HTMLFormElement) => object; props?: svelteHTML.IntrinsicElements['form']; children: Snippet; } = $props(); let form: HTMLFormElement; $effect(() => { if (message) { // エラーメッセージが付いていたらすべてのコントロールをエラー表示にする ['checkbox', 'file-input', 'radio', 'range', 'select', 'ihput', 'textarea', 'toggle'].forEach( (kind) => { for (const elem of form.querySelectorAll(`.${kind}`)) { elem.classList.add(`${kind}-error`); } } ); } }); </script> {#if message}<span class="text-sm text-error">{message}</span>{/if} <form bind:this={form} class="space-y-4" method="POST" use:enhance {...props}> {@render children()} </form>
src/lib/components/InputText.svelte
<script lang="ts"> import type { FormEventHandler } from 'svelte/elements'; import type { Writable } from 'svelte/store'; let { name, label, labelAlt = '', value = $bindable(), disabled = false, errors = undefined, props = {}, oninput = () => {}, }: { name: string; label: string; labelAlt?: string; value: string; disabled?: boolean; errors?: Writable<{}> | undefined; props?: svelteHTML.IntrinsicElements['input']; oninput?: FormEventHandler<HTMLInputElement>; } = $props(); let key = name as keyof typeof errors; </script> <div class="form-control w-full"> <label for={name} class="label"> <span class="label-text">{label}</span> {#if labelAlt}<span class="label-text-alt">{labelAlt}</span>{/if} </label> <input {...{ name, type: 'text', ...props }} bind:value {disabled} {oninput} class="input input-bordered input-primary w-full" class:input-error={errors && $errors && $errors[key]} /> {#if errors && $errors && $errors[key]} <label class="label" for={name}> <span class="label-text-alt text-error">{$errors[key]}</span> </label> {/if} </div>
src/lib/components/InputPassword.svelte
<script lang="ts"> import InputText from '$lib/components/InputText.svelte'; import type { FormEventHandler } from 'svelte/elements'; import type { Writable } from 'svelte/store'; let { name, label, labelAlt = '', value = $bindable(), disabled = false, errors = undefined, props = {}, oninput = () => {}, }: { name: string; label: string; labelAlt?: string; value: string; disabled?: boolean; errors?: Writable<{}> | undefined; props?: svelteHTML.IntrinsicElements['input'] | undefined; oninput?: FormEventHandler<HTMLInputElement>; } = $props(); </script> <InputText {...{ name, label, labelAlt, disabled, errors }} props={{ type: 'password', ...props }} bind:value {oninput} />
SuperForms v2†
https://superforms.rocks/migration-v2
superValidate へ schema を渡す際に、zod のアダプタでラップしなければならなくなった。
import type { Actions, PageServerLoad } from './$types'; import { schema } from './zod-email'; - import { superValidate } from 'sveltekit-superforms/server'; + import { superValidate } from 'sveltekit-superforms'; + import { zod } from 'sveltekit-superforms/adapters'; import { fail, redirect } from '@sveltejs/kit'; import { setFlash } from 'sveltekit-flash-message/server'; import type { purposes } from '$params/emailVerificationPurpose'; import { path } from '$lib'; export const load = (async (event) => { - const form = await superValidate(schema); + const form = await superValidate(zod(schema)); const purpose = event.params.purpose as keyof typeof purposes; return { form, purpose }; }) satisfies PageServerLoad; export const actions: Actions = { default: async (event) => { // フォームデータのバリデーション - const form = await superValidate(event, schema); + const form = await superValidate(event, zod(schema)); const purpose = event.params.purpose as keyof typeof purposes; if (!form.valid) { return fail(400, { form, purpose }); } // TODO: emailVerification レコードを作成 // TODO: メールを送信 setFlash( { type: 'success', message: 'An email was sent to the mail address for mail address validation.', }, event ); throw redirect(302, path('/')); }, };
Counter: 304 (from 2010/06/03),
today: 2,
yesterday: 3