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}ß
|