full-stack-skills/svelte-skills

svelte-template-syntax

Svelte 5 模板语法技能。当用户需要使用 {#if}/{#each}/{#await}/{#snippet} 等块级语法、{@render}/@{html} 等模板标签、bind:、use:/transition:/in:/out:/animate:、style:/class/class:、{@attach} 等指令,或理解属性、事件、文本表达式等基础模板语法时使用。

First seen Jul 13, 2026

Installation

$ npx skills add full-stack-skills/svelte-skills --skill svelte-template-syntax

Summary

Svelte 5 模板语法技能。当用户需要使用 {#if}/{#each}/{#await}/{#snippet} 等块级语法、{@render}/@{html} 等模板标签、bind:、use:/transition:/in:/out:/animate:、style:/class/class:、{@attach} 等指令,或理解属性、事件、文本表达式等基础模板语法时使用。

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from full-stack-skills/svelte-skills · top by installs.

npx skills add full-stack-skills/svelte-skills

Browse all from full-stack-skills/svelte-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 4
License LICENSE
Default branch main
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 17,841 B
  • docs SUMMARY.md 334 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 52 installs

SKILL.md

Svelte Template Syntax Reference (Svelte 5)

本技能覆盖 Svelte 5 模板语法,包括基础标记、块级表达式、模板标签、事件处理、属性绑定、style/class 指令、附件/动作、过渡和动画。

When to use this skill

当用户需要编写或理解 Svelte 组件中的模板语法,包括条件渲染、列表渲染、异步处理、片段复用、事件绑定、双向数据绑定、过渡动画等场景时使用本技能。

Critical: Basic Markup

元素标签

  • 小写标签(<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>使用 &lbrace; 和 &rbrace;</p>

Critical: {#if ...}

条件渲染:

{#if count > 10}
  <p>big</p>
{:else if count > 5}
  <p>medium</p>
{:else}
  <p>small</p>
{/if}

{:else if} 可以链式多次,{:else} 为最终 fallback。

Critical: {#each ...}

列表渲染:

{#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}

带 key 的高效更新

{#each items as item (item.id)}
  <li>{item.name}</li>
{/each}

Key 必须是唯一标识(字符串/数字),用于 Svelte 高效 diff 和更新 DOM(插入/移动/删除而非整体重渲染)。

解构和 rest

{#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}

渲染 N 次

{#each { length: 8 }, rank}
  <div class:black={(rank) % 2 === 1}></div>
{/each}

Critical: {#key ...}

当表达式变化时销毁并重建内容(触发过渡动画):

{#key value}
  <div transition:fade>{value}</div>
{/key}

Critical: {#await ...}

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}

await 表达式(Svelte 5.36+ experimental)

<!-- 需 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。

Critical: {#snippet ...}

可复用的标记片段,取代 Svelte 4 的 Slots:

{#snippet card(item)}
  <div class="card">
    <h3>{item.title}</h3>
    <p>{item.body}</p>
  </div>
{/snippet}

{@render card(item)}

Snippet 作用域

Snippet 可以引用外部变量(script 变量或 {#each} 块级变量),并对同一词法作用域的兄弟/子节点可见:

{#each items as item}
  {#snippet itemCard()}
    <div>{item.name}</div>  <!-- 引用 item -->
  {/snippet}
  {@render itemCard()}
{/each}

显式 vs 隐式 prop

<!-- 隐式 prop(推荐)-->
<Table {data}>
  {#snippet header()}
    <th>Name</th>
  {/snippet}
</Table>

<!-- 显式 prop -->
<Table {data} header={myHeader} />

隐式 children

组件标签内的非 snippet 内容自动成为 children snippet:

<Button>click me</Button>

<!-- Button.svelte -->
<script>
  let { children } = $props();
</script>
<button>{@render children()}</button>

可选 snippet

{@render children?.()}              <!-- 可选链 -->
{#if children}{:else}fallback{/if}  <!-- #if fallback -->

Snippet 类型定义

<script lang="ts">
  import type { Snippet } from 'svelte';
  let { row }: { row: Snippet<[Item]> } = $props();
</script>

导出 snippet(5.5+)

<script module>
  export { mySnippet };
</script>
{#snippet mySnippet()}
  <div>content</div>
{/snippet}

程序化创建(createRawSnippet)

import { createRawSnippet } from 'svelte';
const greet = createRawSnippet<[string]>((name) => ({
  render: () => `<p>Hello, ${name}!</p>`
}));

Critical: {@render ...}

渲染 snippet:

{@render snippetName(args)}
{@render children?.()}  <!-- 可选链安全调用 -->
{@render (cond ? a : b)()}  <!-- 任意表达式 -->

Critical: {@html ...}

插入原始 HTML(注意 XSS 风险):

{@html rawHtml}
  • {expression} 自动转义 HTML 实体
  • {@html expression} 直接插入原始 HTML,需确保内容可信
  • 必须是完整独立 HTML(不能跨 {@html} 拼接标签)
  • 不受 scoped 样式影响 — 用 :global 包裹

Critical: {@attach ...}

元素挂载时运行的函数(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>

带参数的 Attachment Factory

function tooltip(content) {
  return (node) => {
    const tip = createTip(node, { content });
    return { destroy() { tip.destroy(); } };
  };
}

<button {@attach tooltip(content)}>hover</button>

条件 Attachment

<div {@attach enabled && myAttachment}>...</div>

内联 Attachment

<canvas {@attach (canvas) => {
  const ctx = canvas.getContext('2d');
  $effect(() => { ctx.fillStyle = color; /* ... */ });
}}></canvas>

重新运行行为

{@attach foo(bar)}foobar 变化时完整重建(与 action 不同)。

从 Action 转换

import { fromAction } from 'svelte/attachments';
const attach = fromAction(myAction);

Critical: bind: 指令

数据从子级回流到父级:

<!-- 简写 -->
<input bind:value />
<input bind:value={value} />

Function Bindings(5.9+)

<input bind:value={
  () => value,
  (v) => value = v.toLowerCase()
} />

完整 bind: 列表

指令 元素 类型
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 双向

$bindable Props

<!-- 子组件 -->
<script>
  let { value = $bindable() } = $props();
</script>
<input bind:value />

<!-- 父组件 -->
<Child bind:value={text} />

Critical: use: Action(Svelte 5.29 前主推,5.29+ 推荐 {@attach})

<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);参数变化重新运行。

Critical: transition: / in: / out:

元素进入/离开 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 修饰符响应祖先块)

内置 transitions

fade / blur / fly / slide / scale / draw / crossfade(来自 svelte/transition

自定义 transition

function whoosh(node, params) {
  return {
    duration: 400,
    easing: elasticOut,
    css: (t, u) => `transform: scale(${t})`
  };
}

过渡事件

onintrostart / onintroend / onoutrostart / onoutroend

Critical: animate:

keyed each 块内已有元素重排时的动画:

<script>
  import { flip } from 'svelte/animate';
</script>

{#each list as item, i (item)}
  <li animate:flip={{ duration: 300 }}>{item}</li>
{/each}
  • 在新增/删除时触发(用 transition:)
  • 必须 keyed {#each} 的直接子元素
  • flip 是内置(First-Last-In-First-Out 平滑移动)

Critical: style: / class: / class

style: 指令

<div style:color="red" style:width="12rem" style:--columns={n}>
  <!-- 简写、表达式、!important、CSS 自定义属性都支持 -->
</div>

style: 指令优先级高于 style 属性(即使带 !important)。

class 属性(5.16+ 支持对象/数组)

<!-- 对象: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>

ClassValue 类型

<script lang="ts">
  import type { ClassValue } from 'svelte/elements';
  const props: { class: ClassValue } = $props();
</script>

class: 指令(传统,5.16+ 不再推荐)

<div class:cool={cool} class:lame={!cool}>
  <!-- 简写 class:cool -->
</div>

Critical: {@const ...}

⚠️ 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> 的直接子元素。

Critical: {let/const ...}(5.56+ 推荐)

{#each boxes as box}
  {const area = box.width * box.height}
  <p>{area}</p>
{/each}

可在组件任意位置;引用外部值,作用域为词法兄弟/子节点。

响应式:

{let name = $state(user.name)}
{const greeting = $derived(`Hello ${name}`)}

Critical: {@debug ...}

调试标签,变量变化时打印到控制台:

{@debug user, count}       <!-- 指定变量 -->
{@debug}                   <!-- 任意状态变化断点 -->

注意{@debug} 只能接受变量名(不能是表达式)。

Quick Fixes

问题 解决方案
列表不更新 使用带 key 的 {#each items as item (item.id)}
空列表无 fallback {:else} 分支
过渡不触发 {#key value} 包裹触发重建
{@html} 样式不生效 :global 或不用 {@html}
Snippet 不可见 确认在同一词法作用域内
await 同步行为不工作 启用 experimental.async
反向过渡不工作 transition: 而非 in: / out:
animate: 不触发 必须 keyed {#each} + 直接子元素

Gotchas

  1. {#each} 的 key 必须是唯一值 — 使用索引(i)作 key 会导致不正确的高效更新
  2. {@html} 不受 scoped 样式影响 — 插入的 HTML 不包含 Svelte 作用域哈希
  3. {@debug} 不能调试表达式 — 只接受变量名
  4. Snippet 在词法作用域内可见 — 嵌套层级决定可见性
  5. {@attach} 是 Svelte 5.29+ — 旧版本用 use: action
  6. 事件属性以 on 开头onclick 不是 on:click(Svelte 4 语法已废弃)
  7. {@attach} 重新运行完整 setup — 与 action 不重新运行相反;昂贵 setup 用 getBar 形式包装
  8. class 属性的 false/NaN 序列化为字符串 — 5.16+ 用对象形式
  9. bind:group 仅在同一组件内有效 — 跨组件失效
  10. Transitions 在 SSR 期间不运行 — 客户端 hydration 后激活

FAQ

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

可执行的代码示例,见 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

深入技术参考,见 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 事件