count: {count}
Svelte
是一个在 web 中构建用户界面的框架。它使用编译器并使用 HTML,CSS 和 Javascript 来编写声明式组件.
Svelte is a framework for building user interfaces on the web. It uses a compiler to turn declarative components written in HTML, CSS and JavaScript…
.svelte
后缀的文件是用于构建 Svelte 应用的组件。与.vue
文件类似,.svelte
文件包括三部分:HTML
, style
, script
, 其中 style
和 script
是可选的。
// .svelte file
<script module lang="ts">
// Script module
</script>
<script lang="ts">
// Script logic
</script>
<style>
// Style
</style>
Runes
是一个用于在**.svelte**文件中控制 Svelte 编译器的一种特殊符号。
let message = $state("hello");
与 React 的 state 相似,$state
允许我们创建一个响应状态,让 UI 可以动态地响应数据变化。
如果声明的stste
是一个数组或对象,state 则会返回一个proxy
对象
{ obj.test }
// 1
我们也可以使用class
关键字来使用$state
{test.text} {test.num}
$state.raw
如果我们不想在 Array 或 Object 中深度监听
数据变化,我们可以用$state.raw
方法声明变量。
{test.text}
这种方法可以提高
在大型数组和对象中的性能
,避免在这样的数据中进行过度的监听。
$state.snapshot
在$state
进行深度监听数据时,返回的是一个Proxy
对象,如果我们想获取在某一个时间片段中获取该Proxy
对象的静态数据,需要用到$state.snapshot
方法。
类似于 Vue 的computed
和 React 的useMemo
, $derived
可以监听已有的 state 变化而返回经过开发者处理后的数据,例如
{num} {multiple}
$derived.by
$derived
可以捕捉到一些基本的数据类型的变化并返回,如果要进行复杂的数据处理,需要用到$derived.by
方法
{ arr.join(" + ") } = {total}
$effect
函数会追踪那些state和derived发生变化,然后重新执行对应的函数。(类似于 React 中的副作用函数 useEffect)
大多数effects
都用于通过 Svelte 本身创建,例如 <h1>{ title }</h1>
, 当 title 发生变化时,Svelte 内置的 effect 函数则会调用。
同时,我们也可以通过$effect
来自定义 effect 函数。
可以看到当点击按钮 state 发生变化后,先前声明的 effect 函数会进行调用。
需要注意的是,$effect 中只能监听函数内同步读取的依赖,异步读取的数据不会被追踪,例如 await
和 setTimeout
中的数据
testDerive: {testDerive}
顾名思义,$effect.pre
会在 DOM 更新之前调用该 effect 函数。
方法会返回一个布尔值
, 用于查看当前组件是否存在追踪上下文
in template: {$effect.tracking()}
改方法会创建一个非追踪并且不会被自动清除的作用域,适用于手动控制内嵌effect
函数,For example:
Vue
和 React
类似,$props 用于获取传入组件的参数
This is a props: { test }
从$props
中传入的数据,通常是从父组件到子组件单向的数据流。如果我们想创建一个从子组件往上流动的数据,则需要使用$bindable
.
{label}
⚠ 该方法仅在开发时使用
主要用于打印$state和$derived数据的变化,作用是当监听的数据发生变化时,将数据打印到控制台。
$inspect(...).with
则是用于自定义debug的方法
count -= 1}
onincrement={() => count += 1}
>
count: {count}
需要注意的是:
要使用customElement需要在编译配置中加上customElement:true
子组件中的customElement命名必须带有-
,例如my-test
, my-example
// vite.config.ts
export default defineConfig({
plugins: [svelte({
compilerOptions: {
customElement: true
}
})],
})
Template 表达式主要用于在.svelte
文件中编写动态的HTML
。
{#if isShow}
Show
{/if}
{#else}
Hide
{/else}
从Dom结构中可以看到,条件表达式控制是直接改变Dom结构的,并不是简单的display: none
隐藏。
用于遍历数据,对象,Set和Mac并生成相应的HTML结构
{#each list as item (item.key)}
{item.value}
{/each}
{#key}
的作用时:当Key值改变时,表达式中的内容会销毁并重新创建。如果表达式中内容为组件, 则组件会被重新实例化
{#key num}
{num}
{/key}
用于处理Promise异步执行的三个状态
表达式:{#await expression}…{:then name}…{:catch name}…{/await}
{#await promise}
Loading...
{:then value}
Result: {value}
{:catch err}
Error: {err}
{/await}
也可以使用以下表达式
{#await expression then name} … {/await}
{#await expression catch name} … {/await}
#snippet
提供了一个可以在HTML中创建代码片段的方法。
{#snippet name([param1, param2, ... , paramN])}
例如有一个循环生成的HTML组件:
{#each list as item, index (item.key)}
{#if index % 2 === 0}
Even: {item.value}
{:else}
Odd: {item.value}
{/if}
{/each}
利用snippet
可以将循环体内的代码抽离出来:
{#snippet ItemSnip(value: string, index: number)}
{#if index % 2 === 0}
Even: {value}
{:else}
Odd: {value}
{/if}
{/snippet}
{#each list as item, index (item.key)}
{@render ItemSnip(item.value, index)}
{/each}
其中,@render
表达式用于渲染输入的snippet组件,下面会介绍该表达式。
将HTML代码抽离成片段,这也意味着,我们可以导出和传入代码片段
导出snippet:
{#snippet Test(a: number, b: number)}
{a * b}
{/snippet}
导入snippet:
{@render Test(1, 2)}
在snippet
中已经初步介绍了**@render**, 他的唯一作用是用于渲染snippet
{@html content}
console.log
,用于在控制台中打印指定数据与vue
类似,bind表达式用于改变数据流动的方式。通常来说数据流动时单向的,从父节点到子节点。通过bind
表达式,我们可以实现数据从子节点流向父节点。例如:
具体可用的bind:value
的场景,可以参考官网
除此之外,bind
还可以与$bindable
结合用在父子组件的传参当中。
Parent Value: {parentVal}
use:xxx
可以指定一个Action在一个组件渲染之后执行。
{text}
svelte
还提供了一些表达式来使HTML元素拥有简单的过度动画:
transition
, 配合svelte/transition库使用,可以让HTML Element在显示/隐藏时加入动画。
{#if isShow}
Hello World
{/if}
in
和 out
用于分别控制显示/隐藏的动画
{#if isShow}
Hello World
{/if}
animate
svelte
提供了一些特殊的元素来处理特定的情况, 例如处理一些边界情况,绑定window事件等
<svelte:boundary>
允许我们处理一些错误的边界情况,防止整个程序崩溃。
Oops, Something went wrong.
{#if resetFunc !== null}
{/if}
该标签允许开发者在window中添加各种事件,例如: onscroll
, onkeydown
, onclick
,该标签只能定义一个。
同理,<svelte:document>
, \<svelte:body\>
允许开发者在对应的中添加各种事件,例如visibilitychange
, 与<svelte:window>
相同,这些标签在项目中只能定义一个。
document可添加事件:
activeElement
fullscreenElement
pointerLockElement
visibilityState
其中,<svelte:head>
用于定于网页header信息,例如title, description等
<svelte:element>
用于渲染开发者在不同场景下不确定的HTML标签,例如
This is a text.
<svelte:option>
提供一个位置来改变每个组件的配置,例如是否允许runes
, 定于当前组件的namespace
和customElement
等。
具体可能的option有:
<svelte:option runes={true}>
, 是否允许当前组件使用runes
<svelte:option namespace="test">
<svelte:option customElement="my-element">
<svelte:option css="inject">
, 启用改选项会将所有css以内联的方式写入
Svelte
中集成了一个Store对象,用于将声明的动态数据在跨组件之间传输。(与Vuex, Pinia, Redux类似)
完整文档: 完整的用法可以参考官方文档
Writable
: 用于声明一个可以在外部组件改变数据的store
writable 有三个方法,分别是set
, update
, 和subscribe
set
用于直接设置store的数据
import { writable } from 'svelte/store'
const test = writable(1);
set.set(2);
update
同样是设置store的方法
import { writable } from 'svelte/store'
const test = writable(1);
test.update((n) => n + 1);
subscribe
当数据改变时,会触发订阅的方法
import { writable } from 'svelte/store'
const test = writable(1, () => {
console.log(`this is a subscribe logic.`)
});
test.subscribe((n) => {
console.log(`subscribe: ${n}`)
})
test.update((n) => n + 1);
// store.ts
import { writable } from 'svelte/store'
export const testData = writable("Test data")
{$testData}
Readable
创建一个外部组件不可更改的store
derived
与 runes
中的$derived
类似,传入一个store,并返回一个新的store
import { writable, derived } from 'svelte/store'
export const test = writable(0)
export const testDerived = derived(test, ($test) => {
return $test * 2
})
readonly
传入一个store,返回一个只读的store,但原store不会被改变import { writable, readonly } from 'svelte/store'
const testWrite = writable("Hello");
const readonlyTest = readonly(testWrite);
testWrite.set("Test Changed"); // success
readonlyTest.set("TestChanged"); // error
get
用于获取store的值,也可以用$store
代替import { get, writable } 'svelte/store'
const data = writable(0)
console.log(data) // object
console.log($data) // 0
console.log(get(data)) // 0
svelte
组件的生命周期钩子函数只有两部分:创建
和销毁
。
这是因为svelte
认为state的更新与全局组件无关,因此组件的生命周期没有before update
和after update
的部分
Everything in-between — when certain state is updated — is not related to the component as a whole; only the parts that need to react to the state change are notified. This is because under the hood the smallest unit of change is actually not a component, it’s the (render) effects that the component sets up upon component initialization. Consequently, there’s no such thing as a “before update”/"after update” hook.
onMount
组件渲染后调用的钩子函数
onDestory
组件在销毁前调用的钩子函数
tick
它返回一个Promise,当任何state被改变后触发的resolve
,或在下一个微任务触发resolve
。tick
可以替代afterUpdate
钩子函数