- name
- svelte-template-syntax
- license
- Apache-2.0
- description
- Svelte 5 模板语法技能。当用户需要使用 {#if}/{#each}/{#await}/{#snippet} 等块级语法、{@render}/@{html} 等模板标签、bind:、use:/transition:/in:/out:/animate:、style:/class/class:、{@attach} 等指令,或理解属性、事件、文本表达式等基础模板语法时使用。
# Svelte Template Syntax Reference (Svelte 5)
本技能覆盖 Svelte 5 模板语法,包括基础标记、块级表达式、模板标签、事件处理、属性绑定、style/class 指令、附件/动作、过渡和动画。
## When to use this skill
当用户需要编写或理解 Svelte 组件中的模板语法,包括条件渲染、列表渲染、异步处理、片段复用、事件绑定、双向数据绑定、过渡动画等场景时使用本技能。
## Critical: Basic Markup
### 元素标签
- 小写标签(`<div>`)= HTML 元素
- 大写/点号标签(`<Widget>`、`<my.stuff>`)= 组件
### 属性绑定
```svelte
<!-- 布尔属性 -->
<button disabled={!clickable}>...</button>
<input required={false} />
<!-- 简写(同名字相同) -->
<button {disabled}>...</button>
<!-- 展开属性(顺序决定优先级:后写覆盖先写) -->
<Widget {...props} a="b" />
<!-- 布尔属性:truthy 包含,falsy 排除 -->
<div data-active={isActive}> <!-- true → 包含,false/null/undefined → 排除 -->
```
### 事件处理
```svelte
<!-- onclick 是属性,不是指令 -->
<button onclick={() => count++}>+</button>
<input {onkeydown} /> <!-- 简写形式 -->
<button {...handlerProps}> <!-- 展开形式 -->
<!-- 事件委托:大多数事件委托到根节点,无需 stopPropagation -->
<!-- 若需阻止:使用 svelte/events 的 on 函数 -->
```
### 文本表达式
```svelte
<p>{expression}</p> <!-- null/undefined → 省略,其余转字符串 -->
<p>Hello {name}!</p>
<!-- 原始 HTML(注意 XSS) -->
{@html rawContent}
<!-- 转义花括号 -->
<p>使用 { 和 }</p>
```
## Critical: {#if ...}
条件渲染:
```svelte
{#if count > 10}
<p>big</p>
{:else if count > 5}
<p>medium</p>
{:else}
<p>small</p>
{/if}
```
`{:else if}` 可以链式多次,`{:else}` 为最终 fallback。
## Critical: {#each ...}
列表渲染:
```svelte
{#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 的高效更新
```svelte
{#each items as item (item.id)}
<li>{item.name}</li>
{/each}
```
Key 必须是唯一标识(字符串/数字),用于 Svelte 高效 diff 和更新 DOM(插入/移动/删除而非整体重渲染)。
### 解构和 rest
```svelte
{#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 次
```svelte
{#each { length: 8 }, rank}
<div class:black={(rank) % 2 === 1}></div>
{/each}
```
## Critical: {#key ...}
当表达式变化时销毁并重建内容(触发过渡动画):
```svelte
{#key value}
<div transition:fade>{value}</div>
{/key}
```
## Critical: {#await ...}
Promise 异步状态分支:
```svelte
{#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}
```
### 懒加载组件
```svelte
{#await import('./Heavy.svelte') then { default: Component }}
<Component />
{/await}
```
### await 表达式(Svelte 5.36+ experimental)
```svelte
<!-- 需 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:
```svelte
{#snippet card(item)}
<div class="card">
<h3>{item.title}</h3>
<p>{item.body}</p>
</div>
{/snippet}
{@render card(item)}
```
### Snippet 作用域
Snippet 可以引用外部变量(script 变量或 `{#each}` 块级变量),并对**同一词法作用域**的兄弟/子节点可见:
```svelte
{#each items as item}
{#snippet itemCard()}
<div>{item.name}</div> <!-- 引用 item -->
{/snippet}
{@render itemCard()}
{/each}
```
### 显式 vs 隐式 prop
```svelte
<!-- 隐式 prop(推荐)-->
<Table {data}>
{#snippet header()}
<th>Name</th>
{/snippet}
</Table>
<!-- 显式 prop -->
<Table {data} header={myHeader} />
```
### 隐式 children
组件标签内的非 snippet 内容自动成为 `children` snippet:
```svelte
<Button>click me</Button>
<!-- Button.svelte -->
<script>
let { children } = $props();
</script>
<button>{@render children()}</button>
```
### 可选 snippet
```svelte
{@render children?.()} <!-- 可选链 -->
{#if children}{:else}fallback{/if} <!-- #if fallback -->
```
### Snippet 类型定义
```svelte
<script lang="ts">
import type { Snippet } from 'svelte';
let { row }: { row: Snippet<[Item]> } = $props();
</script>
```
### 导出 snippet(5.5+)
```svelte
<script module>
export { mySnippet };
</script>
{#snippet mySnippet()}
<div>content</div>
{/snippet}
```
### 程序化创建(createRawSnippet)
```ts
import { createRawSnippet } from 'svelte';
const greet = createRawSnippet<[string]>((name) => ({
render: () => `<p>Hello, ${name}!</p>`
}));
```
## Critical: {@render ...}
渲染 snippet:
```svelte
{@render snippetName(args)}
{@render children?.()} <!-- 可选链安全调用 -->
{@render (cond ? a : b)()} <!-- 任意表达式 -->
```
## Critical: {@html ...}
插入原始 HTML(**注意 XSS 风险**):
```svelte
{@html rawHtml}
```
- `{expression}` 自动转义 HTML 实体
- `{@html expression}` 直接插入原始 HTML,需确保内容可信
- 必须是**完整独立** HTML(不能跨 `{@html}` 拼接标签)
- 不受 scoped 样式影响 — 用 `:global` 包裹
## Critical: {@attach ...}
元素挂载时运行的函数(Svelte 5.29+),取代 `use:` action:
```svelte
<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
```svelte
function tooltip(content) {
return (node) => {
const tip = createTip(node, { content });
return { destroy() { tip.destroy(); } };
};
}
<button {@attach tooltip(content)}>hover</button>
```
### 条件 Attachment
```svelte
<div {@attach enabled && myAttachment}>...</div>
```
### 内联 Attachment
```svelte
<canvas {@attach (canvas) => {
const ctx = canvas.getContext('2d');
$effect(() => { ctx.fillStyle = color; /* ... */ });
}}></canvas>
```
### 重新运行行为
`{@attach foo(bar)}` 在 `foo` 或 `bar` 变化时**完整重建**(与 action 不同)。
### 从 Action 转换
```js
import { fromAction } from 'svelte/attachments';
const attach = fromAction(myAction);
```
## Critical: bind: 指令
数据从子级回流到父级:
```svelte
<!-- 简写 -->
<input bind:value />
<input bind:value={value} />
```
### Function Bindings(5.9+)
```svelte
<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 | 双向 |
عرض على GitHub