Svelte Tutorial 学习笔记

关于 Svelte 基础以及 Reactivity, Props 和 Logic

2025-06-05

关于学习 [[Svelte]] Tutorial 的简要笔记。

Introduction

Svelte 组件通过 .svelte 文件后缀表示,一个组件可以包含三个部分: script(脚本), styles(样式) 和 markup(HTML),三个部分都不是必须。

如果想在 [[HTML]] 中使用变量,可以花括号 {} 包裹变量名实现:

App.svelte
<script lang="ts">
let name = "immwind"
let src = "image.gif"
</script>
<h1>Hello {name}!</h1>

调用 [[JavaScript]] 的内置对象也同样如此,比如使用 [[toUpperCase]] 方法:

App.svelte
<h1>Hello {name.toUpperCase()}!</h1>

还有元素属性也支持:

App.svelte
<img src={src} alt="image" />

另外在属性名和值相同的情况下,可以使用简写的方式省略属性名:

App.svelte
<img {src} alt="image" />

样式

和在 HTML 中一样在组件中添加 style 标签添加样式(仅在当前组件生效):

<p>This is a paragraph.</p>
<style>
p {
color: goldenrod;
font-family: "Comic Sans MS", cursive;
font-size: 2em;
}
</style>

组件

组件可以通过 import name from . 的形式导入,然后使用 <name /> 的形式调用:

App.svelte
<script lang="ts">
import Nested from './Nested.svelte'
</script>
<p>This is a paragraph.</p>
<Nested />
<style>
p {
color: goldenrod;
font-family: 'Comic Sans MS', cursive;
font-size: 2em;
}
</style>

另外组件名称建议首字母大写,以和 HTML 元素区分。

插入 HTML

通过 [[{@html…}]] 标记可以插入 HTML 语句:

App.svelte
<script>
let string = `this string contains some <strong>HTML!!!</strong>`;
</script>
<p>{string}</p> // this string contains some <strong>HTML!!!</strong>
<p>{@html string}</p> // this string contains some HTML!!!

注:确保 @html 中的字符串是安全的,否则可能会导致 XSS 攻击。

Reactivity

如果希望在 js 文件中使用响应式的方法,可以在文件名中加上 .svelte 后缀,然后通过 import 导入,比如把 utils.js 改成 utils.svelte.js,然后通过 import 导入。

$state

[[$state(…)]] 是 Svelte 中的 rune,当状态值改变时,会自动更新视图:

App.svelte
<script>
let count = $state(0);
function increment() {
count += 1
}
</script>
<button onclick={increment}>
Clicked {count} times.
</button>

深度响应,可以响应值的更新,比如下面例子中是通过 push 方法更新数组,同样会触发响应更新:

App.svelte
<script>
let numbers = $state([1, 2, 3, 4]);
function addNumber() {
numbers.push(numbers.length + 1)
}
</script>
<p>{numbers.join(' + ')} = ...</p>
<button onclick={addNumber}>
Add a number
</button>

$derived

[[$derived(…)]] 从其他状态中派生状态,只读;当依赖的状态值改变时,会自动更新派生状态值:

App.svelte
<script>
let numbers = $state([1, 2, 3, 4]);
let total = $derived(numbers.reduce((t, n) => t + n, 2))
function addNumber() {
numbers.push(numbers.length + 1);
}
</script>
<p>{numbers.join(' + ')} = {total}</p>
<button onclick={addNumber}>
Add a number
</button>

在上面的例子中,当 numbers 更新时,total 会同样会自动更新。

$inspect

[[$inspect(…)]] 可以实时打印出状态值的变更,仅在开发环境有效,生产环境不会有任何操作:

App.svelte
<script>
let count = $state(0);
</script>
<button onclick={() => count += 1}>
Clicked {count} times.
</button>
<p>count: {$inspect(count)}</p>

在开发时如果需要用到 console.log 打印变量,可以尝试使用 $inspect 代替,还支持通过 .with(fn) 的方式追踪变化是从哪里开始的:

App.svelte
$inspect(numbers).with(console.trace);

$effect

$inspect 类似,[[$effect(…)]] 可以监听状态值的变更,但是不同的是 $effect 还支持对变化进行响应:

App.svelte
<script lang="ts">
let count = $state(0);
$effect(() => {
console.log(count); // 打印日志
document.title = `计数: ${count}`; // 比如修改DOM
});
</script>
<button onclick={() => count += 1}>
Click Add {count}
</button>

在上面的例子中,当 count 更新时,会自动调用 $effect 中的回调函数,打印日志和修改 DOM。

Props

在组件中传递数据,可以使用 [[$props(…)]] rune 接收属性,比如下面是接收 anser 属性:

Nested.svelte
<script>
let { answer } = $props();
</script>
<p>The answer is {answer}</p>

在引用的一方传递对应属性的值:

App.svelte
<script>
import Nested from './Nested.svelte';
</script>
<Nested answer={42} />

设置默认值:

Nested.svelte
<script>
let { answer = "a mystery" } = $props();
</script>

设置后如果没有传递值,则使用默认值:

App.svelte
<Nested answer={42} />
<Nested answer />

同样也可以设置多个属性:

PackageInfo.svelte
<script>
let { name, version, description, website } = $props();
</script>

有多个属性的情况下,传递值时可以直接展开:

App.svelte
<script>
import PackageInfo from './PackageInfo.svelte';
const pkg = {
name: 'svelte',
version: 5,
description: 'blazing fast',
website: 'https://svelte.dev'
};
</script>
<PackageInfo {...pkg} />

或者是通过解构的方式:

PackageInfo.svelte
<script>
let stuff = $props();
</script>

然后通过 stuff.name 方式调用。

Logic

  • {#...}: 开启块
  • {:...}: 继续块
  • {/...}: 关闭块

if

[[if]] 条件语句。

if:

{#if count > 10 }
<p>{count} is greater than 10</p>
{/if}

if..else:

{#if count > 10}
<p>{count} is greater than 10</p>
{:else}
<p>{count} is between 0 and 10</p>
{/if}

else if:

{#if count > 10}
<p>{count} is greater than 10</p>
{:else if count < 5}
<p>{count} is less than 5</p>
{:else}
<p>{count} is between 0 and 10</p>
{/if}

each

通过 [[{]] 可以在模板中遍历数组,以减少重复代码:

<script>
const colors = ['red', 'orange', 'yellow', 'green'];
let selected = $state(colors[0]);
</script>
<h1 style="color: {selected}">Pick a colour}</h1>
<div>
{#each colors as color, i}
<button
style="background: {color}"
aria-label={color}
aria-current={selected === color}
onclick={() => {
selected = color;
}}
>{i + 1}</button>
{/each}
</div>

可以为 each 每次迭代指定一个唯一的键:

<script>
import Thing from './Thing.svelte';
let things = $state([
{ id: 1, name: 'apple' },
{ id: 2, name: 'banana' },
{ id: 3, name: 'carrot' },
{ id: 4, name: 'doughnut' },
{ id: 5, name: 'egg' }
]);
</script>
<button onclick={() => things.shift()}>
Remove first thing
</button>
{#each things as thing (thing.id)}
<Thing name={thing.name} />
{/each}

wait

语法: #await:then:catch/await

当需要 [[{]] 等待异步操作完成时,可以使用 #await 语句:

App.svelte
<script>
import { roll } from './utils.js';
let promise = $state(roll());
</script>
<button onclick={() => promise = roll()}>
roll the dice
</button>
{#await promise}
<p>...rolling</p>
{:then number}
<p>you rolled a {number}!</p>
{:catch error}
<p style="color: red">{error.message}</p>
{/await}

await 和 catch 都可以省略:

App.svelte
{#await promise then number}
<p>you rolled a {number}!</p>
{/await}

参考