Svelte 5 入门教程
列表渲染 {#each}
本教程共 50 篇 · 第 13 篇 · 更新于 2026-08-05 · 约 5 分钟阅读
SvelteSvelte 5模板语法列表渲染{#each}
本节目标:掌握 {#each} 列表渲染语法,理解 key 的作用、index 参数、空列表处理。学完你能用循环渲染任何列表数据。
基本语法
用 {#each} 遍历数组渲染列表:
<script>
let items = $state([
{ name: '苹果', qty: 3 },
{ name: '香蕉', qty: 5 },
{ name: '橙子', qty: 2 }
]);
</script>
<h1>购物清单</h1>
<ul>
{#each items as item}
<li>{item.name} x {item.qty}</li>
{/each}
</ul>
{#each 数组 as 变量} 会遍历数组的每个元素,把当前元素赋值给 变量,然后渲染块内内容。
index 参数
可以同时获取索引(从 0 开始):
<ul>
{#each items as item, i}
<li>{i + 1}. {item.name} x {item.qty}</li>
{/each}
</ul>
输出:
1. 苹果 x 3
2. 香蕉 x 5
3. 橙子 x 2
key 的作用
默认情况下,{#each} 用数组位置来匹配新旧项目。当数组顺序变化(插入、删除、排序)时,可能导致状态错乱或动画异常。
给每个项目加一个 key,Svelte 就能正确追踪每个项目:
<script>
let todos = $state([
{ id: 1, text: '学习 Svelte', done: false },
{ id: 2, text: '写项目', done: false },
{ id: 3, text: '部署上线', done: false }
]);
</script>
<ul>
{#each todos as todo (todo.id)}
<li>
<input type="checkbox" bind:checked={todo.done} />
{todo.text}
</li>
{/each}
</ul>
key 写在元素后面的括号里:{#each 数组 as 变量 (key)}。
Notekey 必须能唯一标识每个项目。推荐用数据自带的
id字段。用 index 做 key 在列表有增删排序时会有问题,不推荐。
有 key 和没 key 的区别
<script>
let items = $state([
{ id: 'a', text: '第一个' },
{ id: 'b', text: '第二个' },
{ id: 'c', text: '第三个' }
]);
function removeFirst() {
items.shift();
}
</script>
<!-- ✅ 有 key:删除第一项后,剩余项保持不变,只移除第一项的 DOM -->
{#each items as item (item.id)}
<div>{item.text}</div>
{/each}
<!-- ❌ 无 key:删除第一项后,Svelte 可能更新所有 DOM 节点的内容 -->
{#each items as item}
<div>{item.text}</div>
{/each}
解构写法
{#each} 支持直接解构数组元素:
<!-- 解构对象 -->
{#each items as { id, name, qty }, i (id)}
<li>{i + 1}. {name} x {qty}</li>
{/each}
<!-- 解构数组 -->
{#each pairs as [a, b]}
<li>{a} + {b} = {a + b}</li>
{/each}
<!-- rest 解构 -->
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
空列表 {:else}
当数组为空(或 null/undefined)时,可以用 {:else} 显示兜底内容:
<script>
let todos = $state([]);
</script>
{#each todos as todo}
<p>{todo.text}</p>
{:else}
<p>今天没有任务!</p>
{/each}
Tip
{:else}在数组为空、null、undefined时都会显示。非常适合做「暂无数据」的提示。
不需要项目变量的循环
如果你只需要重复渲染固定次数,不需要 as:
<div class="chess-board">
{#each { length: 8 }, rank}
{#each { length: 8 }, file}
<div class:black={(rank + file) % 2 === 1}></div>
{/each}
{/each}
</div>
这里 { length: 8 } 会生成 8 个迭代,rank 和 file 是索引(0-7)。
遍历其他可迭代对象
{#each} 不只能遍历数组,还支持:
- 类数组对象(有
length属性) Map和Set- 任何可迭代对象(Iterable)
<script>
let fruits = new Set(['苹果', '香蕉', '橙子']);
</script>
<ul>
{#each fruits as fruit}
<li>{fruit}</li>
{/each}
</ul>
数组更新与渲染
因为 $state 创建的数组是深层响应代理,任何数组方法(push、pop、splice、sort 等)都会触发重新渲染:
<script>
let items = $state(['A', 'B', 'C']);
function add() {
items.push('D'); // 触发更新
}
function remove() {
items.pop(); // 触发更新
}
function sort() {
items.sort().reverse(); // 触发更新
}
function reset() {
items = ['A', 'B', 'C']; // 整体替换也触发更新
}
</script>
<button onclick={add}>添加</button>
<button onclick={remove}>删除</button>
<button onclick={sort}>排序</button>
<button onclick={reset}>重置</button>
<ul>
{#each items as item, i (item + i)}
<li>{item}</li>
{/each}
</ul>
本节回顾
{#each 数组 as 变量}遍历数组,{#each 数组 as 变量, 索引}获取序号- key 写在括号里:
{#each 数组 as 变量 (key)},唯一标识每个项目 - 有 key 时增删排序不会错乱,强烈推荐始终使用 key
- 支持解构:
{#each 数组 as { id, name }, i (id)} {:else}在数组为空/null/undefined 时显示兜底内容{#each { length: n }, i}可以重复渲染 n 次不需要数据- 支持 Set、Map 等可迭代对象
$state数组的 push/pop/sort 等方法都触发更新