首页 / Svelte 5 入门教程 / 列表渲染 {#each}

Svelte 5 入门教程

列表渲染 {#each}

本教程共 50 篇 · 第 13 篇 · 更新于 2026-08-05 · 约 5 分钟阅读

SvelteSvelte 5模板语法列表渲染{#each}

13. 列表渲染 {#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)}

Note

key 必须能唯一标识每个项目。推荐用数据自带的 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} 在数组为空、nullundefined 时都会显示。非常适合做「暂无数据」的提示。

不需要项目变量的循环

如果你只需要重复渲染固定次数,不需要 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 个迭代,rankfile 是索引(0-7)。

遍历其他可迭代对象

{#each} 不只能遍历数组,还支持:

  • 类数组对象(有 length 属性)
  • MapSet
  • 任何可迭代对象(Iterable)
<script>
	let fruits = new Set(['苹果', '香蕉', '橙子']);
</script>

<ul>
	{#each fruits as fruit}
		<li>{fruit}</li>
	{/each}
</ul>

数组更新与渲染

因为 $state 创建的数组是深层响应代理,任何数组方法(pushpopsplicesort 等)都会触发重新渲染:

<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 等方法都触发更新