初识 Svelte

Runes

#state

svelte 的状态,横向对比 Vue 的 ref,react 的 state

和 Vue React 不同的是 svelte 的值可以直接修改,UI即可响应,并且 $state 深度响应,你可以使用对象或者数组的 api 来更改值,如果不想深度响应就用 $state.raw

1
2
3
4
let a = $state([1,2,3])
let b = $state.snapshot([1,2,3])
console.log("代理的", a);
console.log("静态的", b);

使用 $state.snapshot 不会被代理

$derived

1
2
3
4
<script>
let count = $state(0);
let doubled = $derived(count * 2);
</script>

计算属性,当计算过程中的 state 值发生变化会重新计算,使用 $derived 计算不能传入带有副作用的计算表达式,并且在传入的计算表达式中不能修改 state 的值

如果计算过程有点复杂,无法用一个表达式解决问题,还可以使用 $derived.by 支持传入一个有返回值 function,

$effect

$effect 会在组件安装到 DOM 后执行,$effect 不是必须放在最顶层,他可以放在任何地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
let count = $state(0);
let milliseconds = $state(1000);


function testEffect () {
$effect(() => {
const interval = setInterval(() => {
count += 1;
}, milliseconds);

return () => {
clearInterval(interval);
};
});
}
testEffect()
</script>

<h1>{count}</h1>

这么写虽然没什么意义,但可以证明 $effect 即使在一个 function 里也不会有任何问题

$effect 会自动识别内部的 $state、$props、$derived 并作为依赖项,当他们更新时 $effect 会重新执行

$effect 可以返回一个 function,该 function 会在销毁时执行,不过以下这种情况

1
2
3
4
5
6
7
8
9
10
11
12
<script>
let count = $state({
name: 'jack'
});

$effect(() => {
console.log(count);
})
</script>

<h1>{count.name}</h1>
<button onclick={() => count.name = 'lucy'}>click</button>

当点击 button 更改 state 的 name 时,$effect 是没办法识别到深层的更新的,处理这个问题有两种方式,第一种就是直接在 $effect 使用 count.name ,另外一种就是使用 $derived 计算属性

1
2
3
4
5
6
7
8
9
10
11
12
<script>
let count = $state({
name: 'jack'
});
let name = $derived(count.name)
$effect(() => {
console.log(name);
})
</script>

<h1>{count.name}</h1>
<button onclick={() => count.name = 'lucy'}>click</button>

还有一种情况是

1
2
3
4
5
6
7
<script>
$effect(() => {
if (a || b) {
console.log('inside if block');
}
});
</script>

分别将 a 和 b 修改为 true ,但是打印只会执行一次,文档给出的解释是 effect 的执行效果取决于上次读到的值,大概就是说 a 已经为 true, b 不会对 effect 的执行产生更改,所以更改 b 不会触发 effect 执行

如果想在 DOM 更新前做点事,可以使用 $effect.pre,用法和 $effect 完全一致

另外 svelte 不建议直接在 $effect 同步状态,也就是修改 state 的值。

$props

用来接收父组件传来的值,该值一般来说不希望被修改,并且修改一般是没作用的,唯一特殊的情况是传来的值是 $state 值虽然被修改但是会抛出警告该值不是属于你的

$bindable

想允许自组件修改父组件的值,需要使用 $bindable

子组件

1
2
3
4
5
<script lang="ts">
let { value = $bindable() } = $props();
</script>

<input bind:value={value} />

父组件

1
2
3
4
5
6
7
<script>
import FancyInput from "../FancyInput.svelte";
let message = $state('hello');
</script>

<FancyInput bind:value={message}/>
<p>{message}</p>

如果想给 $bindable 设置一个默认值,直接给 $bindable 入参即可 $bindable(默认值)

$inspect

在参数发生变化时,会在控制台打印。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script lang="ts">
const obj = $state( {
name: 'jack',
age: 18,
hobby: ['run', 'jump']
})

const addHobby = () => {
obj.hobby.push('ball')
}

$inspect(obj.hobby)
</script>

<main>
<div>{obj.hobby.join(',')}</div>
<button onclick={addHobby}>addHobby</button>
</main>

以上代码,在初始化时,$inspect 会输出 init 的值,当点击按钮进行更新时,会再次输出 update 的值

控制台输出

$inspect 执行返回一个 with 方法,该方法接收一个函数,函数有两个参数,一个是 type 初始化值为 init,更新时值为 update,第二个参数就是新的值

Template syntax

$snippet

使用 $snippet 声明代码片段,通过 @render 渲染使用代码片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{#snippet figure(image)}
<figure>
<img src={image.src} alt={image.caption} width={image.width} height={image.height} />
<figcaption>{image.caption}</figcaption>
</figure>
{/snippet}

{#each images as image}
{#if image.href}
<a href={image.href}>
{@render figure(image)}
</a>
{:else}
{@render figure(image)}
{/if}
{/each}ß

初识 Svelte
https://l1ushun.github.io/2024/10/26/svelte/
作者
liu shun
发布于
2024年10月26日