Summary
Svelte 5 模板语法技能。当用户需要使用 {#if}/{#each}/{#await}/{#snippet} 等块级语法、{@render}/@{html} 等模板标签、bind:、use:/transition:/in:/out:/animate:、style:/class/class:、{@attach} 等指令,或理解属性、事件、文本表达式等基础模板语法时使用。
full-stack-skills/svelte-skills
Svelte 5 模板语法技能。当用户需要使用 {#if}/{#each}/{#await}/{#snippet} 等块级语法、{@render}/@{html} 等模板标签、bind:、use:/transition:/in:/out:/animate:、style:/class/class:、{@attach} 等指令,或理解属性、事件、文本表达式等基础模板语法时使用。
npx skills add full-stack-skills/svelte-skills --skill svelte-template-syntax
Svelte 5 模板语法技能。当用户需要使用 {#if}/{#each}/{#await}/{#snippet} 等块级语法、{@render}/@{html} 等模板标签、bind:、use:/transition:/in:/out:/animate:、style:/class/class:、{@attach} 等指令,或理解属性、事件、文本表达式等基础模板语法时使用。
Related neighbors and high-traction skills in the same topics — useful to compare before installing.
Replace with description of the skill and when Claude should use it.
64K installsCopy a Google Docs template, fill in content, and share with collaborators.
27.7K installsGoogle Model Armor: Create a new Model Armor template.
26.8K installsScaffold new Agent Skills with proper frontmatter, directory structure, and bundled resource or…
9K installsCreates self-contained PPT template SKILLS (not presentations) from user-provided PowerPoint te…
2K installsProduce the final PlanetScale best-practices report after running the inventory and relevant re…
13 installsOther skills from full-stack-skills/svelte-skills · top by installs.
npx skills add full-stack-skills/svelte-skills
Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.
main
Files included with this skill beyond the listing page.
SKILL.md
17,841 B
SUMMARY.md
334 B
本技能覆盖 Svelte 5 模板语法,包括基础标记、块级表达式、模板标签、事件处理、属性绑定、style/class 指令、附件/动作、过渡和动画。
当用户需要编写或理解 Svelte 组件中的模板语法,包括条件渲染、列表渲染、异步处理、片段复用、事件绑定、双向数据绑定、过渡动画等场景时使用本技能。
<div>)= HTML 元素<Widget>、<my.stuff>)= 组件<!-- 布尔属性 -->
<button disabled={!clickable}>...</button>
<input required={false} />
<!-- 简写(同名字相同) -->
<button {disabled}>...</button>
<!-- 展开属性(顺序决定优先级:后写覆盖先写) -->
<Widget {...props} a="b" />
<!-- 布尔属性:truthy 包含,falsy 排除 -->
<div data-active={isActive}> <!-- true → 包含,false/null/undefined → 排除 -->
<!-- onclick 是属性,不是指令 -->
<button onclick={() => count++}>+</button>
<input {onkeydown} /> <!-- 简写形式 -->
<button {...handlerProps}> <!-- 展开形式 -->
<!-- 事件委托:大多数事件委托到根节点,无需 stopPropagation -->
<!-- 若需阻止:使用 svelte/events 的 on 函数 -->
<p>{expression}</p> <!-- null/undefined → 省略,其余转字符串 -->
<p>Hello {name}!</p>
<!-- 原始 HTML(注意 XSS) -->
{@html rawContent}
<!-- 转义花括号 -->
<p>使用 { 和 }</p>
条件渲染:
{#if count > 10}
<p>big</p>
{:else if count > 5}
<p>medium</p>
{:else}
<p>small</p>
{/if}
{:else if} 可以链式多次,{:else} 为最终 fallback。
列表渲染:
{#each items as item}
<li>{item.name}</li>
{/each}
<!-- 带索引 -->
{#each items as item, i}
<li>{i + 1}: {item.name}</li>
{/each}
<!-- 空列表 fallback -->
{#each todos as todo}
<p>{todo.text}</p>
{:else}
<p>No tasks!</p>
{/each}
{#each items as item (item.id)}
<li>{item.name}</li>
{/each}
Key 必须是唯一标识(字符串/数字),用于 Svelte 高效 diff 和更新 DOM(插入/移动/删除而非整体重渲染)。
{#each items as { id, name, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
{#each items as [id, ...values]}
<li><span>{id}</span></li>
{/each}
{#each { length: 8 }, rank}
<div class:black={(rank) % 2 === 1}></div>
{/each}
当表达式变化时销毁并重建内容(触发过渡动画):
{#key value}
<div transition:fade>{value}</div>
{/key}
Promise 异步状态分支:
{#await promise}
<p>loading...</p>
{:then value}
<p>result: {value}</p>
{:catch error}
<p>error: {error.message}</p>
{/await}
<!-- 简洁形式(无 pending UI) -->
{#await promise then value}
<p>{value}</p>
{/await}
<!-- 仅错误处理 -->
{#await promise catch error}
<p>{error.message}</p>
{/await}
{#await import('./Heavy.svelte') then { default: Component }}
<Component />
{/await}
<!-- 需 svelte.config.js 中启用 experimental.async -->
<svelte:boundary>
<p>{await fetchData()}</p>
{#snippet pending()}<p>loading</p>{/snippet}
</svelte:boundary>
特性:同步更新、并发执行(独立 await)、<svelte:boundary> pending snippet、fork() API。
可复用的标记片段,取代 Svelte 4 的 Slots:
{#snippet card(item)}
<div class="card">
<h3>{item.title}</h3>
<p>{item.body}</p>
</div>
{/snippet}
{@render card(item)}
Snippet 可以引用外部变量(script 变量或 {#each} 块级变量),并对同一词法作用域的兄弟/子节点可见:
{#each items as item}
{#snippet itemCard()}
<div>{item.name}</div> <!-- 引用 item -->
{/snippet}
{@render itemCard()}
{/each}
<!-- 隐式 prop(推荐)-->
<Table {data}>
{#snippet header()}
<th>Name</th>
{/snippet}
</Table>
<!-- 显式 prop -->
<Table {data} header={myHeader} />
组件标签内的非 snippet 内容自动成为 children snippet:
<Button>click me</Button>
<!-- Button.svelte -->
<script>
let { children } = $props();
</script>
<button>{@render children()}</button>
{@render children?.()} <!-- 可选链 -->
{#if children}{:else}fallback{/if} <!-- #if fallback -->
<script lang="ts">
import type { Snippet } from 'svelte';
let { row }: { row: Snippet<[Item]> } = $props();
</script>
<script module>
export { mySnippet };
</script>
{#snippet mySnippet()}
<div>content</div>
{/snippet}
import { createRawSnippet } from 'svelte';
const greet = createRawSnippet<[string]>((name) => ({
render: () => `<p>Hello, ${name}!</p>`
}));
渲染 snippet:
{@render snippetName(args)}
{@render children?.()} <!-- 可选链安全调用 -->
{@render (cond ? a : b)()} <!-- 任意表达式 -->
插入原始 HTML(注意 XSS 风险):
{@html rawHtml}
{expression} 自动转义 HTML 实体{@html expression} 直接插入原始 HTML,需确保内容可信{@html} 拼接标签):global 包裹元素挂载时运行的函数(Svelte 5.29+),取代 use: action:
<script>
/** @type {import('svelte/attachments').Attachment} */
function tooltip(node) {
const tip = createTip(node);
return { destroy() { tip.destroy(); } };
}
</script>
<button {@attach tooltip()}>hover</button>
function tooltip(content) {
return (node) => {
const tip = createTip(node, { content });
return { destroy() { tip.destroy(); } };
};
}
<button {@attach tooltip(content)}>hover</button>
<div {@attach enabled && myAttachment}>...</div>
<canvas {@attach (canvas) => {
const ctx = canvas.getContext('2d');
$effect(() => { ctx.fillStyle = color; /* ... */ });
}}></canvas>
{@attach foo(bar)} 在 foo 或 bar 变化时完整重建(与 action 不同)。
import { fromAction } from 'svelte/attachments';
const attach = fromAction(myAction);
数据从子级回流到父级:
<!-- 简写 -->
<input bind:value />
<input bind:value={value} />
<input bind:value={
() => value,
(v) => value = v.toLowerCase()
} />
| 指令 | 元素 | 类型 |
|---|---|---|
bind:value |
input/textarea/select | 双向 |
bind:checked |
input[type=checkbox] | 双向 |
bind:indeterminate |
input[type=checkbox] | 双向 |
bind:group |
radio/checkbox 组 | 双向 |
bind:files |
input[type=file] | 双向 |
bind:open |
details | 双向 |
bind:value |
select multiple | 数组 |
bind:currentTime/paused/volume/muted/playbackRate |
audio/video | 双向 |
bind:duration/buffered/seeking/ended/readyState/played |
audio/video | 只读 |
bind:videoWidth/Height |
video | 只读 |
bind:naturalWidth/Height |
img | 只读 |
bind:innerHTML/innerText/textContent |
contenteditable | 双向 |
bind:clientWidth/Height/offsetWidth/Height/contentRect |
块级元素 | 只读 |
bind:this |
元素/组件 | 引用 |
bind:innerWidth/Height |
svelte:window | 双向 |
bind:scrollX/Y |
svelte:window | 双向 |
bind:online |
svelte:window | 双向 |
bind:fullscreenElement |
svelte:document | 双向 |
bind:visibilityState |
svelte:document | 双向 |
<!-- 子组件 -->
<script>
let { value = $bindable() } = $props();
</script>
<input bind:value />
<!-- 父组件 -->
<Child bind:value={text} />
<script>
/** @type {import('svelte/action').Action<HTMLElement, {color: string}>} */
function paint(node, data) {
node.style.color = data.color;
}
</script>
<div use: paint={{ color: 'red' }}>red text</div>
Action 只调用一次(不 SSR);参数变化不重新运行。
元素进入/离开 DOM 时的过渡:
<script>
import { fade, fly } from 'svelte/transition';
let visible = $state(false);
</script>
{#if visible}
<div transition:fade>fades in and out</div>
<div in:fly={{ y: 200 }} out:fade>flies in, fades out</div>
{/if}
transition: 双向(可中途反向)in: / out: 单向(并行不打断)|global 修饰符响应祖先块)fade / blur / fly / slide / scale / draw / crossfade(来自 svelte/transition)
function whoosh(node, params) {
return {
duration: 400,
easing: elasticOut,
css: (t, u) => `transform: scale(${t})`
};
}
onintrostart / onintroend / onoutrostart / onoutroend
keyed each 块内已有元素重排时的动画:
<script>
import { flip } from 'svelte/animate';
</script>
{#each list as item, i (item)}
<li animate:flip={{ duration: 300 }}>{item}</li>
{/each}
{#each} 的直接子元素flip 是内置(First-Last-In-First-Out 平滑移动)<div style:color="red" style:width="12rem" style:--columns={n}>
<!-- 简写、表达式、!important、CSS 自定义属性都支持 -->
</div>
style: 指令优先级高于 style 属性(即使带 !important)。
<!-- 对象:truthy key 添加 -->
<div class={{ cool, lame: !cool }}>...</div>
<!-- 数组:falsy 过滤后合并 -->
<div class={[faded && 'saturate-0', large && 'scale-200']}>...</div>
<!-- 嵌套扁平化 -->
<div class={['btn', { primary: true }, ['rounded', { large }]]}>...</div>
<script lang="ts">
import type { ClassValue } from 'svelte/elements';
const props: { class: ClassValue } = $props();
</script>
<div class:cool={cool} class:lame={!cool}>
<!-- 简写 class:cool -->
</div>
⚠️ Legacy 语法,建议用
{const x = $derived(y)}替代
在块级作用域内定义常量:
{#each boxes as box}
{@const area = box.width * box.height}
<p>{box.width} × {box.height} = {area}</p>
{/each}
仅能为 {#if}、{#each}、{#snippet}、<Component />、<svelte:boundary> 的直接子元素。
{#each boxes as box}
{const area = box.width * box.height}
<p>{area}</p>
{/each}
可在组件任意位置;引用外部值,作用域为词法兄弟/子节点。
响应式:
{let name = $state(user.name)}
{const greeting = $derived(`Hello ${name}`)}
调试标签,变量变化时打印到控制台:
{@debug user, count} <!-- 指定变量 -->
{@debug} <!-- 任意状态变化断点 -->
注意:{@debug} 只能接受变量名(不能是表达式)。
| 问题 | 解决方案 |
|---|---|
| 列表不更新 | 使用带 key 的 {#each items as item (item.id)} |
| 空列表无 fallback | 加 {:else} 分支 |
| 过渡不触发 | 用 {#key value} 包裹触发重建 |
{@html} 样式不生效 |
用 :global 或不用 {@html} |
| Snippet 不可见 | 确认在同一词法作用域内 |
await 同步行为不工作 |
启用 experimental.async |
| 反向过渡不工作 | 用 transition: 而非 in: / out: |
animate: 不触发 |
必须 keyed {#each} + 直接子元素 |
{#each} 的 key 必须是唯一值 — 使用索引(i)作 key 会导致不正确的高效更新{@html} 不受 scoped 样式影响 — 插入的 HTML 不包含 Svelte 作用域哈希{@debug} 不能调试表达式 — 只接受变量名{@attach} 是 Svelte 5.29+ — 旧版本用 use: actionon 开头 — onclick 不是 on:click(Svelte 4 语法已废弃){@attach} 重新运行完整 setup — 与 action 不重新运行相反;昂贵 setup 用 getBar 形式包装class 属性的 false/NaN 序列化为字符串 — 5.16+ 用对象形式bind:group 仅在同一组件内有效 — 跨组件失效Q: {#each} 中 key 的作用是什么? A: 帮助 Svelte 高效 diff:当数据变化时,通过 key 精确识别哪个元素变化(插入/移动/删除),避免整体重渲染。
Q: Snippet 和 Slot 有什么区别? A: Snippet 是 Svelte 5 的新机制,更强大灵活。Slot 只能传递标记;Snippet 可以接收参数、可在定义处直接使用、可传递到组件、词法作用域共享。
Q: {@html} 和普通文本插值的区别? A: {expr} 自动 HTML 转义;{@html expr} 直接插入原始 HTML。原始 HTML 不受 scoped 样式影响。
Q: 什么时候用 {#key}? A: 当需要表达式变化时触发动画/重建时,例如路由切换、用户切换等场景。
Q: use: 和 {@attach} 怎么选? A: Svelte 5.29+ 推荐 {@attach} — 更灵活(参数响应、可用于组件)。旧库只提供 action 时用 fromAction(act) 包装。
Q: transition: vs in: / out:? A: transition: 双向可反向;in: / out: 单向并行播放不互相打断。
Q: animate: 和 transition: 区别? A: animate: 是 keyed each 中已有元素重排时;transition: 是元素进入/离开 DOM 时。
Q: 什么时候用 await 表达式 vs {#await} 块? A: 新代码推荐 await 表达式(需 experimental.async),支持同步更新、并发。旧代码或简单分支可用 {#await} 块。
可执行的代码示例,见 examples/ 目录:
| 文件 | 内容 |
|---|---|
basic-markup.md |
标签、属性、展开、文本表达式、注释、事件委托 |
if-await-snippet.md |
{#if}/{#each}/{#await}/{#snippet} 基础到递归 |
snippet-advanced.md |
Snippet 作用域、显式/隐式 prop、可选、类型化、模块导出、createRawSnippet |
render-tags.md |
{@render}/{@html}/{@debug}/{@const}/{@attach} |
attachments.md |
{@attach} factory / inline / 条件 / 转换 action |
use-action.md |
use: 指令 + Action 类型签名 + use→{@attach} 迁移 |
transitions.md |
transition:/in:/out: 参数、自定义函数、事件、crossfade |
animations.md |
animate:flip / 自定义函数 / 与 transition 组合 |
await-expressions.md |
synchronized / 并发 / pending / fork / SSR |
style-class.md |
style: 指令、class 对象/数组、class: 传统、ClassValue |
bind-directives.md |
bind:value/checked/group/files/this/offsetWidth/Function bindings |
event-handlers.md |
onclick、事件冒泡、委托、window/document 事件 |
深入技术参考,见 references/ 目录:
| 文件 | 内容 |
|---|---|
basic-markup.md |
标签语义、属性规则、事件委托、注释、SSR 行为 |
if-await-each.md |
{#if}/{#else}/{#each}/{#await} 完整语法及 keyed 机制 |
snippet-render.md |
Snippet 定义/调用/递归/传参,@render/@const 类型签名 |
snippet-advanced.md |
显式/隐式 prop、可选 snippet、createRawSnippet、模块导出、Snippet vs Slot |
special-tags.md |
{@html}/{@debug}/{@attach}/svelte:element |
attachments.md |
{@attach} 语义、reactive 重运行、fromAction 迁移、svelte/attachments API |
transitions.md |
局部/全局、内置 transitions、自定义函数 css/tick、events、crossfade |
animations.md |
animate:flip、自定义函数(from/to DOMRect)、与 transition 组合 |
await-expressions.md |
同步更新、并发、$effect.pending()、settled()、fork、SSR |
style-class.md |
style: 优先级、class 对象/数组/ClassValue、!important 规则 |
bind-reference.md |
所有 bind: 指令完整列表及适用元素 |
event-reference.md |
事件处理:onclick vs on:click、window/document 事件 |