svelte-legacy-apis について
Svelte 4のLegacyコンポーネントを保守し、Svelte 5へ移行する開発者向けのリファレンススキルです。リアクティビティやコンポーネントAPIに加え、Svelte 3から4への移行事項も扱います。
主な機能
- 01Legacyのリアクティビティと代入規則の解説
- 02props、イベント、slotのリファレンス
- 03Legacy APIとRunesの対応表
- 04Svelte 3から4への移行情報
- 05例、チェックリスト、トラブルシューティング
活用シーン
- 01`$:`やトップレベルの`let`を使うコンポーネントの保守
- 02`export let`から`$props()`への置き換え
- 03配列変更後に更新されない問題の調査
- 04Svelte 3から4へのアップグレード時の変更点確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ full-stack-skills/ svelte-skills --skill svelte-legacy-apisインストール後、Claude Code で/svelte-legacy-apisを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlexamples/legacy-mode.mdmigration-scripts.mdmigration-v4.mdREADME.mdstore-comparison.mdreferences/legacy-mode-reference.mdmigration-reference.mdmigration-v4-reference.mdREADME.mdstore-reference.md詳細
Svelte Legacy API リファレンス(Svelte 4 → Svelte 5)
このスキルでは、Svelte 4 の Legacy API を扱います。これらの API は Svelte 5 でも Legacy Mode で引き続き利用できますが、Runes への移行が推奨されています。このスキルは Legacy コードの理解と移行のみを目的としており、新しいコードでは Runes を使用してください。
このスキルを使用する場面
ユーザーが Svelte 4 プロジェクトの保守、$: リアクティブステートメントや export let props、createEventDispatcher、Slots の理解、または Legacy コードから Svelte 5 Runes Mode への移行を必要としている場合に、このスキルを使用してください。
重要:リアクティブな let 宣言
Legacy Mode(Runes を使用していないコンポーネント)では、トップレベルの let 宣言は自動的にリアクティブになります:
<script>
let count = 0; // 自动响应式
</script>
<button on:click={() => count++}>
{count}
</button>
Svelte 5 の Runes Mode:
let count = $state(0)。
配列/オブジェクトの変更に関する注意
Legacy のリアクティビティは代入に基づいています。配列メソッド(.push()、.splice())では更新がトリガーされません:
<script>
let items = [1, 2, 3];
function addItem() {
items.push(4); // ❌ 不触发更新
items = items; // ✅ 触发
// 或
items = [...items, 4]; // ✅ 触发
}
</script>
重要:リアクティブな $: ステートメント
Legacy Mode では、$: ラベル付きステートメントによってリアクティブステートメントを作成します:
<script>
let a = 1;
let b = 2;
$: sum = a + b; // 依赖 a, b
$: console.log(`sum = ${sum}`); // 依赖 sum
</script>
トポロジカルソート
$: ステートメントは依存関係に基づいてトポロジカルソートされます(順序は関係ありません):
$: z = y; // z 依赖 y
$: y = x; // y 依赖 x
$: x = 5; // x 无依赖
ブロックステートメント
$: {
total = 0;
for (const item of items) total += item.value;
}
重要:export let
Legacy Mode では、export let を使って props を宣言します:
<script>
export let name; // 必需 prop
export let age = 18; // 带默认值
export let items = []; // 数组默认值
</script>
Svelte 5 Runes:
let { name, age = 18 } = $props()
再エクスポート(API)
<script>
export function greet(name) {
alert(`Hello ${name}!`);
}
export const VERSION = '1.0';
</script>
親コンポーネントから bind:this を通じて呼び出します:
<script>
let greeter;
</script>
<Greeter bind:this={greeter} />
<button on:click={() => greeter.greet('world')}>greet</button>
prop の名前変更
<script>
export { foo as bar }; // 对外叫 bar,内部叫 foo
</script>
重要:$$props / $$restProps
$$props(渡されたすべての props を取得)
<script>
let { foo, ...rest } = $$props;
// foo 是显式 prop,rest 是其余所有 props
</script>
Svelte 5 Runes:
let { foo, ...rest } = $props()
$$restProps(残りの props のみ)
<script>
export let required;
let { ...rest } = $$props;
</script>
重要:createEventDispatcher
Legacy のイベントディスパッチの仕組み(Svelte 5 では callback props に置き換えられています):
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function handleClick() {
dispatch('change', { value: 42 });
dispatch('complete'); // 无数据
}
</script>
<button on:click={handleClick}>发送</button>
イベントの使用
<Component on:change={(e) => console.log(e.detail)} />
型付け(TypeScript)
<script lang="ts">
const dispatch = createEventDispatcher<{
change: { value: number };
complete: null;
}>();
</script>
Svelte 5 での推奨方法:callback props を直接使用します:
<Component onChange={(detail) => handle(detail)} />
重要:Slots
<slot> は Legacy のコンテンツ受け渡しの仕組みです(Svelte 5 では Snippets に置き換えられています):
デフォルト slot
<div class="card">
<slot />
</div>
<Component>
<p>内容</p>
</Component>
名前付き slot
<slot name="footer" />
<slot name="header" />
<Component>
<p slot="header">头部</p>
<p slot="footer">尾部</p>
</Component>
Slot props(slot にデータを渡す)
<slot value={count} onClick={handleClick} />
<Component let:value let:onClick>
<p>{value}</p>
<button on:click={onClick}>click</button>
</Component>
注意:Svelte 5 では、default slot props には
let:プレフィックスが必要です。named slots では slot props を公開できません。
$$slots(slot の有無を確認)
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
$: if (!$$slots.footer) {
// footer slot 未被提供
}
</script>
重要:svelte:component / svelte:self
<svelte:component>(動的コンポーネント)
<script>
let Current = FirstComponent;
$: Current = showSecond ? SecondComponent : FirstComponent;
</script>
<svelte:component this={Current} />
Svelte 5 での推奨方法:
<svelte:element this={Current} />
<svelte:self>(再帰コンポーネント)
<script>
import { svelte:self } from 'svelte';
</script>
<svelte:self />
重要:svelte:fragment
<slot name="title" />
<div>
<slot name="body" />
</div>
<Component>
<h1 slot="title">Title</h1>
<svelte:fragment slot="body">
<p>多元素</p>
<p>不会被包裹</p>
</svelte:fragment>
</Component>
重要:v5 移行のクイックリファレンス
| Legacy(Svelte 4) | モダン(Svelte 5) |
|---|---|
let count = 0(トップレベルでリアクティブ) | let count = $state(0) |
$: x = expr | let x = $derived(expr) |
$: { if (cond) x = v } | $effect(() => { if (cond) x = v }) |
export let prop | let { prop } = $props() |
$$props | let { ...rest } = $props() |
<slot /> | {@render children()} |
createEventDispatcher | callback props |
on:click={fn} | onclick={fn} |
<svelte:component this={x}> | <svelte:element this={x}> |
<svelte:self> | import Self from './This.svelte' + <Self> |
class:cool={isCool} | class={{ cool: isCool }} |
let:slotProp={value} | Snippets による自動受け渡し |
注意点
- Legacy の
$:は代入に基づきます — 配列の.push()などのメソッドでは更新がトリガーされません - Slot props の動作は異なります — default slot では slot props にアクセスできますが、named slots ではできません
createEventDispatcherは非推奨です — Svelte 5 では推奨されていません。新しいコードでは callback props を使用してください$$slotsはコンパイル時に注入されます — script 内で直接使用でき、$:内からも参照できます<svelte:self>はインポートが必要です —import { svelte:self } from 'svelte'
重要:Svelte 4 への移行(Svelte 3 → Svelte 4)
Svelte 3 プロジェクトを保守していて Svelte 4 にアップグレードする場合は、以下の破壊的変更を一つずつ確認する必要があります。詳しい比較と自動化スクリプトについては、examples/migration-v4.md と references/migration-v4-reference.md を参照してください。ソースの基準は /tmp/svelte-llms.txt:6182 です。
推奨される開始方法:
npx svelte-migrate@latest svelte-4—— このスクリプトで大半の項目を自動処理できます。それ以外は手動で対応する必要があります。
最低バージョン要件
{
"engines": { "node": ">=16" },
"devDependencies": {
"svelte": "^4.0.0",
"@sveltejs/kit": ">=1.20.4",
"vite-plugin-svelte": ">=2.4.1",
"svelte-loader": ">=3.1.8",
"rollup-plugin-svelte": ">=7.1.5",
"typescript": ">=5"
}
}
主要破坏性变更速查
| 类别 | Svelte 3 | Svelte 4 |
|---|---|---|
| Bundler browser condition | 可选 | Rollup browser: true / Webpack conditionNames: ['browser'] |
| CJS 输出 | 支持 | 移除(编译器、svelte/register、runtime) |
createEventDispatcher 类型 | 可选 payload | 严格:T | undefined 可选;T 必填;null 禁 detail |
Action / ActionReturn | params 隐式 any | 必须显式 Action<HTMLElement, T> |
onMount 异步清理 | 不报错 | 返回 Promise 的清理函数会被忽略(TS 报错) |
| Custom Element | <svelte:options tag="x" /> | <svelte:options customElement="x" />(字符串或对象) |
SvelteComponentTyped | 与 SvelteComponent 并存 | 弃用,统一为 SvelteComponent |
| Transitions 默认范围 | 任意祖先块触发 | 仅直接父块触发,加 |global 恢复旧行为 |
| Default slot bindings | named slot 也可见 | 仅 default slot 可见 |
| Preprocessor 顺序 | markup×N, script×N, style×N | markup, script, style ×N(每个 preprocessor 内部三段) |
eslint-plugin-svelte3 | 推荐 | 弃用,改 eslint-plugin-svelte |
inert on outroing | 不应用 | 默认应用(无障碍改进) |
classList.toggle | 兼容老浏览器 | 需要 polyfill |
CustomEvent | 兼容老浏览器 | 需要 polyfill |
derived falsy 值 | 警告 | 抛错 |
svelte/internal 类型 | 暴露 | 已移除 |
svelte.JSX 命名空间 | 用 | 改 svelteHTML / svelte/elements |
库作者 peerDependencies | ^3 | ^3 || ^4 |
自动化脚本能处理的
tag=→customElement=(字符串形式)SvelteComponentTyped→SvelteComponentAction/ActionReturn泛型参数- Transitions 默认行为(添加
|global修饰符)
需要手动处理的
- Bundler
browsercondition 配置(Rollup / Webpack) - 严格类型错误的修复(
createEventDispatcherpayload) onMount异步清理改同步- Preprocessor
name属性 + 顺序调整 - ESLint 插件替换 + 配置迁移
derivedfalsy 值边界条件
Custom Element 升级
<svelte:options tag="my-counter" />
<svelte:options customElement={{ tag: 'my-counter', props: { initial: { type: Number } } }} />
onMount 异步清理反模式
// ❌ v4 报错且永远不清理
onMount(async () => { const data = await fetch('/api'); setup(data); return () => teardown(); });
// ✅ 同步返回值
onMount(() => { fetch('/api').then(setup); return () => teardown(); });
Preprocessor 顺序:MDsveX 必须最前
// svelte.config.js
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import { mdsvex } from 'mdsvex';
export default {
preprocess: [
mdsvex({ name: 'mdsvex' }), // 先 markup 再 script
vitePreprocess({ name: 'vite' })
]
};
v3:所有 markup → 所有 script → 所有 style。v4:每个 preprocessor 内部 markup → script → style,再下一个。每个 preprocessor 必须声明
name。
库作者的 peerDependencies
{
"peerDependencies": { "svelte": "^3.0.0 || ^4.0.0" },
"devDependencies": { "svelte": "^4.0.0" }
}
Run-it-Yourself Checklist
npm install svelte@^4 # 1. 升级依赖
npx svelte-migrate@latest svelte-4 # 2. 跑迁移脚本
npx svelte-check # 3. 看 TypeScript
npm test # 4. 跑测试
如大量出现 Action 泛型错误,说明 migration 脚本漏掉;手动补 <HTMLElement, YourType>。
进一步 v5 迁移在 svelte-runes / svelte-misc 中;本技能只覆盖 Svelte 4 升级。
Quick Fixes
| 问题 | 解决 |
|---|---|
数组 .push() 不更新 | items = [...items, newItem] 或 items = items |
| slot 样式不生效 | 样式隔离 scoped,不影响 slot 内容 |
$$restProps 类型错误 | 确认是 export let 而非 let |
| EventDispatcher 类型问题 | 升级 TypeScript 到 5+ |
FAQ
Q: 什么时候必须用 Legacy API? A: 维护未迁移的 Svelte 4 项目时。新项目应使用 Runes。
Q: 可以混合使用 Legacy 和 Runes 吗?
A: 可以。一个组件使用 $state 即进入 Runes Mode,未使用的部分用 Legacy 语法。
Q: $: 和 $derived 的区别?
A: 功能相似,但 $derived 是 Svelte 5 的 Runes API,更显式和可控。$: 在 Legacy Mode 可用。
Q: Slot 和 Snippet 哪个更好? A: Snippets 更强大灵活。Slot 适合简单内容传递;Snippet 可接收参数、可在定义处使用、类型安全更好。
相互参照
このスキルは Svelte Legacy の保守専用で、以下のスキルと連携します。
- Svelte 5 のエントリーポイントと学習パス →
svelte-awesomeスキルに引き継ぎます。インストール:npx skills add full-stack-skills/svelte-skills --skill svelte-awesome。 - Svelte 4 → Svelte 5 の移行対応表 →
svelte-miscスキル(migration 章)に引き継ぎます。インストール:npx skills add full-stack-skills/svelte-skills --skill svelte-misc。 - Runes のリアクティビティ(
$state/$derived/$effect)→svelte-runesスキルに引き継ぎます。インストール:npx skills add full-stack-skills/svelte-skills --skill svelte-runes。 - Stores(writable / readable / derived)→
svelte-lifecycleスキルに引き継ぎます。インストール:npx skills add full-stack-skills/svelte-skills --skill svelte-lifecycle。 - テンプレート構文(snippet / event / bind)→
svelte-template-syntaxスキルに引き継ぎます。インストール:npx skills add full-stack-skills/svelte-skills --skill svelte-template-syntax。 - リアクティビティが更新されない問題のデバッグ /
$inspect→svelte-lifecycleスキルに引き継ぎます。インストール:npx skills add full-stack-skills/svelte-skills --skill svelte-lifecycle。 - コンポーネントを Web Component にコンパイルする →
svelte-miscスキル(Custom Elements 章)に引き継ぎます。インストール:npx skills add full-stack-skills/svelte-skills --skill svelte-misc。 - 初期化と移行スクリプト →
svelte-cliスキル(sv migrate svelte-5)に引き継ぎます。インストール:npx skills add full-stack-skills/svelte-skills --skill svelte-cli。
例
Legacy API を使った実践的な例:
- examples/README.md - 例のエントリーポイント
- examples/migration-scripts.md - 移行ワークフロー(svelte-migrate、let→$state、$:→$derived など)
- examples/legacy-mode.md - Legacy モードの使用方法、Legacy と runes の併用
- examples/store-comparison.md - Svelte 4 と Svelte 5 における Store パターン
- examples/migration-v4.md - Svelte 3 → Svelte 4 の移行に関する実践的な例 12 件(bundler 設定、厳密な型付け、Custom Element、transition、slot、preprocessor など)
参考資料
リファレンスドキュメント一式:
- references/README.md - リファレンスのエントリーポイント
- references/migration-reference.md - 移行前後のコードを含む完全な移行リファレンス
- references/legacy-mode-reference.md - Legacy モードの動作と制限事項
- references/store-reference.md - Store API リファレンス(writable、readable、derived、カスタム Store)
- references/migration-v4-reference.md - Svelte 3 → Svelte 4 の完全な対応リファレンス(最低バージョン、bundler、厳密な型付け、Custom Element、slot、preprocessor、その他の破壊的変更、ライブラリ作者向けガイド)