svelte-misc について
Svelte 5 開発者向けのリファレンススキルです。TypeScript、カスタムエレメント、ブラウザー対応、推奨パターン、Svelte 4 からの移行情報をまとめています。
主な機能
- 01TypeScript の高度な型とコンポーネントパターン
- 02カスタムエレメントとライフサイクルの解説
- 03ブラウザー対応と機能別の例外
- 04Svelte 5 のリアクティビティ推奨パターン
- 05Svelte 4 から 5 への移行ガイド
活用シーン
- 01ジェネリックまたはラッパーコンポーネントの型付け
- 02Web Components の構築
- 03対象ブラウザーでの機能対応確認
- 04Svelte 4 プロジェクトの段階的な移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ full-stack-skills/ svelte-skills --skill svelte-miscインストール後、Claude Code で/svelte-miscを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlexamples/best-practices.mdbrowser-support.mdcustom-elements.mddebug-html.mddynamic-components.mdmigration-v4.mdREADME.mdtypescript-advanced.mdtypescript.mdreferences/best-practices.mdbrowser-support.mdcustom-elements.mddynamic-reference.mdREADME.mdtypescript-advanced.mdtypescript-reference.md詳細
Svelte Misc Reference (Svelte 5)
このスキルでは、Svelte 5 の TypeScript 高度なサポート、カスタム要素(Web Components)、ブラウザー互換性マトリックス、Svelte 4→5 移行ガイド、公式ベストプラクティス、よくある質問を扱います。
このスキルを使う場面
ユーザーが TypeScript の型注釈(ジェネリクス、ラッパーコンポーネント、DOM 型の拡張)を必要とする場合、$host() / $bindable / extend を使ってカスタム Web コンポーネントを作成する場合、ブラウザー互換性マトリックスと機能ごとの例外を確認する場合、Svelte 4 から Svelte 5 に移行する場合、または Svelte 5 推奨の $state / $derived / $effect パターンに従う場合に、このスキルを使用します。
重要:TypeScript 高度な機能
Svelte は TypeScript をネイティブでサポートしています(Vite プロジェクトでは設定不要です)。<script lang="ts"> で型注釈を有効にします。enum/パラメーター・プロパティなどの実行時機能が必要な場合は、プリプロセッサーが必要です。
<script lang="ts">
let name: string = 'world';
function greet(name: string) { alert(`Hello, ${name}!`); }
</script>
<button onclick={(e: Event) => greet((e.target as HTMLElement).innerText)}>
{name}
</button>
lang="ts"がデフォルトでサポートするのは、「型だけが消去される」機能(注釈、インターフェース、ジェネリクス)のみです。実行時のコード機能(enum、private foo = 1形式のパラメーター・プロパティ)には、vitePreprocess({ script: true })の設定が必要です。
Props の型指定と透過
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
requiredProperty: number;
optionalProperty?: boolean;
snippetWithStringArgument: Snippet<[string]>;
eventHandler: (arg: string) => void;
[key: string]: unknown; // 配合 ...rest 透传其他属性
}
let { requiredProperty, optionalProperty, snippetWithStringArgument,
eventHandler, ...rest }: Props = $props();
</script>
ジェネリックコンポーネント
<script> の generics 属性では、関数のジェネリクスと同じ構文を使用できます。
<script lang="ts" generics="Item extends { text: string }">
interface Props { items: Item[]; select(item: Item): void; }
let { items, select }: Props = $props();
</script>
{#each items as item}
<button onclick={() => select(item)}>{item.text}</button>
{/each}
ラッパーコンポーネント(ネイティブ属性の転送)
<script lang="ts">
import type { HTMLButtonAttributes } from 'svelte/elements';
let { children, ...rest }: HTMLButtonAttributes = $props();
</script>
<button {...rest}>{@render children?.()}</button>
専用のインターフェースがない要素には、SvelteHTMLElements['div'] 形式を使用します。Component / ComponentProps<T> は、動的コンポーネントの制約や既存コンポーネントの props の抽出に使用します。
クラス内の $state
class Counter {
count = $state() as number; // 无初值类型会扩为 undefined
constructor(initial: number) { this.count = initial; }
increment() { this.count += 1; }
}
svelte/elements の拡張
/// file: additional-svelte-typings.d.ts
declare module 'svelte/elements' {
export interface SvelteHTMLElements { 'custom-button': HTMLButtonAttributes; }
export interface HTMLAttributes<T> { globalattribute?: string; }
}
export {}; // 必须——否则会覆盖原模块
重要:カスタム要素(Web Components)
基本的な使い方
<svelte:options customElement="my-element" />
<script>
let { name = 'world' } = $props();
</script>
<h1>Hello {name}!</h1>
<slot />
インポートすると自動的に customElements.define() が登録されます。利用側では DOM 属性を読み書きできます。
const el = document.querySelector('my-element');
el.name = 'everybody'; // 触发 shadow DOM 更新
props を明示的に宣言する必要があります(
let props = $props()だけでは不十分です)。そうしないと、Svelte は公開するキーを認識できません。
customElement オプション
<svelte:options customElement={{
tag: 'my-counter',
shadow: 'open',
props: { count: { type: 'Number', reflect: true, attribute: 'start' } },
extend: (Ctor) => class extends Ctor { /* ... */ }
}} />
| フィールド | 値 | デフォルト | 役割 |
|---|---|---|---|
tag | string | — | タグ名。設定すると自動登録されます |
shadow | 'none' | 'open' | 'closed' | ShadowRootInit | 'open' | Shadow root の設定 |
props.<name>.attribute | string | 小文字の prop 名 | カスタム属性名 |
props.<name>.reflect | boolean | false | prop の値を HTML 属性に反映します |
props.<name>.type | 'String' | 'Boolean' | 'Number' | 'Array' | 'Object' | 'String' | 属性 ↔ prop の型変換 |
extend | (Ctor) => class | — | 生成されたクラスをラップします(ライフサイクル/ElementInternals を注入できます) |
$host() Rune とライフサイクル
<svelte:options customElement="tooltip" />
<script>
import { $host } from 'svelte';
let { text = 'hi' } = $props();
function focus() { $host().focus(); }
</script>
<button onclick={focus}>{text}</button>
- 内部コンポーネントは
connectedCallbackの後、次の tick に作成されます。挿入前に設定した属性はバッファリングされ、適用されます。 - Shadow DOM の更新は次の tick までバッチ処理されます。DOM の移動によって不要なアンマウントは発生しません。
disconnectedCallbackの後、次の tick に破棄されます。コンポーネントのメソッドはマウント後にのみ使用できます。より早く公開するには、extendクラス内で宣言してください。
重要な注意事項
- スタイルは「スコープ化」ではなくカプセル化されます。グローバル CSS は shadow root を通過できません。
- CSS は個別の
.cssファイルではなく、JS 文字列としてインライン化されます。 - SSR には適していません(JS の読み込み前は shadow DOM が表示されません)。
- スロットの内容は積極的にレンダリングされます(Svelte のデフォルトの遅延レンダリングとは異なります)。
{#each}内の<slot>では内容は複製されません。 let:ディレクティブはカスタム要素では機能しません。Svelte のcontextはカスタム要素の境界を越えられません。on*属性はイベントリスナーとして解釈されるため、名前に使用しないでください。
重要:ブラウザーサポート
Svelte 5 の対象は Baseline 2020 です。
| ブラウザー | 最低バージョン |
|---|---|
| Chrome / Edge | 87 |
| Firefox | 83 |
| Safari | 14 |
| Opera | 73 |
| Opera (Android) | 62 |
| Samsung Internet | 14.0 |
| Android WebView | 87 |
| Internet Explorer | 非サポート |
例外
| 機能 | Chrome/Edge | Firefox | Safari |
|---|---|---|---|
$state.snapshot | 98 | 94 | 15.4 |
bind:devicePixelContentBoxSize | — | 93 | 非サポート |
flip(svelte/animate 由来) | — | 126 | — |
SSR でのブラウザー API 保護
<script>
import { onMount } from 'svelte';
onMount(() => {
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
});
</script>
<svelte:window>/<svelte:document>を優先してください。Svelte が SSR とアンマウントを自動的に処理します。
重要:ベストプラクティス
$state — 必要なときだけ宣言する
更新をトリガーする値($effect / $derived / テンプレート)のみを $state として宣言します。再代入のみを行う大きなオブジェクト(API レスポンス)には、Proxy のオーバーヘッドを避けるため $state.raw を使用します。
let user = $state.raw<User | null>(null);
純粋な計算には $effect より $derived
let square = $derived(num * num); // 好
// let square; $effect(() => { square = num * num; }); // 差
複雑なロジックには $derived.by(() => { ... }) を使用します。Derived は書き込み可能です。オブジェクト/配列の結果は深くプロキシ化されません。
$effect はエスケープハッチです
優先するツール:外部ライブラリーとの同期 → {@attach ...}、ユーザー入力への応答 → ハンドラー、デバッグログ → $inspect / $inspect.trace、外部ソースの監視 → createSubscriber。effect はサーバーでは実行されないため、effect を if (browser) { ... } で囲まないでください。
Props は変化するものとして扱い、イベントには属性構文を使う
let { type } = $props();
let color = $derived(type === 'danger' ? 'red' : 'green');
グローバルイベントのリスナーには <svelte:window> / <svelte:document> を使用します。onMount で手動バインドしないでください。Slots の代わりに Snippets を使用し、{#each ... (item.id)} では安定したキーを使います(index は使用不可)。style:-- で JS 変数を CSS カスタムプロパティに渡します。子コンポーネントのスタイルを通過させるには :global ではなく CSS 変数を使用します。モジュールレベルの共有状態の代わりに createContext を使用します(型安全性を確保し、SSR でユーザー間の情報漏えいを防ぎます)。
新しいコードでは Legacy を避ける
on:click → onclick;export let → $props();$: → $derived / $effect;<slot> → snippet;use:action → {@attach ...}。
重要:Svelte 4 移行ガイド
最小バージョン
| 依存関係 | 要件 |
|---|---|
| Node | 16+ |
| SvelteKit | 1.20.4+(使用する場合) |
vite-plugin-svelte | 2.4.1+ |
| TypeScript | 5+ |
核心迁移对照
| Svelte 4 | Svelte 5 |
|---|---|
let x = 0(顶层响应式) | let x = $state(0) |
$: x = a + b | let x = $derived(a + b) |
$: { ... } | $effect(() => { ... }) |
export let prop | let { prop } = $props() |
<slot /> | {@render children()} |
createEventDispatcher | callback props |
on:click={fn} | onclick={fn} |
自动迁移工具
npx svelte-migrate@latest svelte-5 my-project
重要行为变化
- 转换默认
local(外层控制流不影响子 transition)。 - 默认插槽绑定不会进入具名插槽。
- 预处理器顺序:markup → script → style(按声明顺序逐个执行)。
createEventDispatcher严格模式下检查可选 / 必传 / 无参类型(之后用 callback props 替代)。Action/ActionReturn默认参数类型为undefined,需要参数时显式声明。SvelteComponentTyped已弃用,统一用SvelteComponent。- CJS 输出与
svelte/register被移除。 - 删除
svelte.JSX,改用svelteHTML/svelte/elements。 - 打包器必须指定
browser条件(SvelteKit / Vite 自动;Rollup 用@rollup/plugin-node-resolve设browser: true;Webpack 在conditionNames加"browser")。
Critical: Svelte 5 Migration Guide (Concept Summary)
Runes 系统:$state / $derived / $effect 替代隐式响应式;Snippets 替代 Slots;事件属性替代 on:;$effect 显式追踪依赖;$state 对对象/数组做深层 Proxy。
| 移除 | 替代 |
|---|---|
let 顶层隐式响应式 | $state |
$: 语句 | $derived / $effect |
export let / $$props / $$restProps | $props() |
createEventDispatcher | callback props / $bindable |
| Slots | Snippets |
on:event | onevent |
beforeUpdate / afterUpdate | $effect.pre / $effect |
新增:$state.eager、$effect.pre、$effect.tracking、$state.snapshot、hydratable、fork()、{#key} 过渡触发、<svelte:boundary>、createContext。
FAQ
Q: Svelte 和 Vue/React 的核心区别? A: Svelte 无虚拟 DOM——编译时生成精确 DOM 更新代码,运行时零框架开销。
Q: Svelte 5 和 Svelte 4 可以共存吗?
A: 可以。Svelte 5 仍支持 Legacy Mode,Svelte 4 的 $: 与 export let 可继续用;可在同一项目渐进迁移。
Q: 如何升级到 Svelte 5?
A: ① 升级 svelte + sveltekit / vite-plugin-svelte;② 跑 npx svelte-migrate@latest svelte-5;③ 逐步迁到 Runes Mode。
Q: Svelte 支持 SSR / TypeScript 吗?
A: 都原生支持。SSR 用 render() 或 SvelteKit;TS 直接用 <script lang="ts">,CI 跑 svelte-check。
Q: Svelte 有虚拟 DOM 吗?
A: 没有。Svelte 编译器生成精确 DOM 操作代码(element.textContent = ...),而非通过 vDOM diffing。
Q: 哪些浏览器支持 Svelte 5? A: Baseline 2020(Chrome / Edge 87+、Firefox 83+、Safari 14+)。不支持 IE;少数功能需更高版本(见上表)。
Q: 自定义元素的样式如何控制?
A: 默认 shadow root 模式 'open',全局 CSS 不会穿透。可用 shadow: 'none' 继承全局样式;用 CSS 变量穿透 scoped 样式。
Quick Fixes
| 问题 | 解决方案 |
|---|---|
| Svelte 4 代码报错 | 确认组件未使用 $state(进入 Runes Mode) |
$: 不生效 | 升级到 Svelte 5,Legacy Mode 仍支持 |
| TypeScript 报错 | 确认 <script lang="ts"> 和 TypeScript 5+ |
| 迁移后打包变大 | 检查 tree-shaking 配置 |
属性以 on 开头被监听 | 重命名避免 on* 前缀 |
| 自定义元素 SSR 没内容 | 预渲染不支持 shadow DOM;等 JS 加载 |
| generic component 类型报错 | 检查 generics 字符串(与函数泛型同语法) |
Gotchas
- Svelte 5 自动进入 Runes Mode — 只要使用了任何 Rune(
$state等)即为 Runes Mode。 - Legacy Mode — 可通过
<svelte:options runes={false}>强制启用。 on:click已废弃 — Legacy Mode 仍可用,但会警告。createEventDispatcher已弃用 — 新代码用 callback props。$bindable(default)— 仅在父组件未传 prop 时作 fallback;父组件传undefined会保留 undefined。bind:this泛型组件 — 实例类型为Component<Props> | null,需要断言。- 自定义元素 inline CSS — 没有可被外部样式表链接的
.css文件。 - 服务端 effect 不运行 — 不要用
if (browser)包 effect 主体。
例
Svelte 5 の機能を実際に使った例です。
- 動的コンポーネント:svelte:element と {#key} ブロック
- デバッグと HTML:@html、@debug、@const
- TypeScript:
- TypeScript の基本 — 型付き props、ジェネリクス、イベントハンドラー、コンポーネント参照
- TypeScript の応用 — 14 のパターン:プリプロセッサ、ジェネリック制約、ラッパーコンポーネント、
Component型、クラス内の$state、svelte/elementsの拡張、svelte-check、$bindable、フォームの状態
- カスタム要素:カスタム要素 — 12 のパターン:
customElementオプション、$host()、型変換、リフレクション、extend、ElementInternals、スロットのセマンティクス - ベストプラクティス:Svelte 5 のベストプラクティス — 14 のパターン:
$state.raw、$derived.by、キー付き each ブロック、CSS 変数、createContext、非同期 Svelte、レガシー機能の置き換え - 移行:Svelte 3→4 の移行 — 10 のパターン:
tag→customElement、より厳格な型、トランジションのスコープ、プリプロセッサの順序、CJS の削除 - ブラウザーサポート:ブラウザーサポート — 7 のパターン:機能検出、ポリフィル、バンドラーのターゲット、SSR ガード、
$state.snapshotのフォールバック、Playwright のバージョン固定
リファレンス
詳細なリファレンスドキュメントです。
- 動的コンポーネント:動的コンポーネントのリファレンス —
svelte:element、{#key}、{@html}、@debug、@const - TypeScript:
- TypeScript の基本 — 型推論、スニペット、イベントの型、ジェネリクス
- TypeScript の応用 —
Component型、svelte/elements、DOM 型の拡張、svelte-check、エラー
- カスタム要素:カスタム要素 —
customElementの API、$host()ルーン、ライフサイクル、注意点、ElementInternals - ベストプラクティス:ベストプラクティス —
$state/$derived/$effect、props、イベント、スニペット、each ブロック、CSS 変数、コンテキスト、非同期処理 - ブラウザーサポート:ブラウザーサポート — 最低バージョンの一覧、機能ごとの例外、ポリフィル、SSR の安全性、バンドラーの設定
Quick Links
| Feature | Reference |
|---|---|
svelte:element | Dynamic Components |
{#key} | Dynamic Components |
{@html} / @debug / @const | Dynamic Components |
| TS basics | TypeScript Basics |
| TS advanced | TypeScript Advanced |
| Generic components | TypeScript Advanced |
| Wrapper components | TypeScript Advanced |
Component type | TypeScript Advanced |
| DOM augmentation | TypeScript Advanced |
customElement options | Custom Elements |
$host() rune | Custom Elements |
| Web Component lifecycle | Custom Elements |
$state / $derived / $effect patterns | Best Practices |
| Async Svelte | Best Practices |
| Browser matrix | Browser Support |
| Feature exceptions | Browser Support |
| Polyfills | Browser Support |