Skip to content

Svelte-基础篇

如 React、Vue 等框架是在运行时通过虚拟 DOM 进行差异对比,然后更新真实 DOM。
而 Svelte 的核心思想是在编译时就把组件代码转换成高效、命令式的 JavaScript 代码,直接操作真实 DOM。

响应式 / 深度状态 • Svelte

基础

大概过了一下 Svelte的基础教程:

  • .svelte 单文件组件,类HTML,
  • 动态属性、样式
  • 嵌套组件

状态

  • $state响应式状态:let numbers = $state([1, 2]);,也是通过 proxy 实现
  • $derived派生状态:$derived(numbers.reduce((t, n) => t + n, 0));类似computed
  • $effect: Svelte 替你创建的、响应状态变化而更新 DOM,这就是一个 effect — 但你也可以使用 $effect 符文创建自己的 effect,这个有点像 useEffect,也有点像 vue3 watch,可以返回一个清理函数

props

  • $props(): 在子组件中显式声明接收来自父组件的 props

逻辑块

1 条件 if else

svelte
{#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 5 and 10</p> 
{/if}

2 遍历 each

svelte
<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 块 • Svelte 教程 Svelte 的工作方式不同:组件只”运行”一次,后续更新是”细粒度的”。这使得操作更快,并给你更多控制权。 可以看这个例子,它不像 react 那样,props 变了就重新渲染

3 异步 Await 代码块

svelte
{#await promise}
	<p>...rolling</p>
{:then number}
	<p>you rolled a {number}!</p>
{:catch error}
	<p style="color: red">{error.message}</p>
{/await}

事件

1 小写风格 onpointermove

svelte
<div onpointermove={onpointermove}>
	The pointer is at {Math.round(m.x)} x {Math.round(m.y)}
</div>

2 事件捕获 onkeydowncapture 事件捕获,在事件名称后加 capture

3 事件回调可以通过 props 传递,和 react 差不多,在单项数据流的基础上实现子组件触发父组件更新。vue里边也可以这样干,但更推荐的方式是基于事件的方式,子组件发出自定义事件,父组件监听自定义事件,通过这种方式由父组件自己来做更新。

绑定

这里就紧接着上文了,通常来说,众多框架都是自上而下的单向数据流。父组件为子组件设置 props,组件为元素设置 props, 反过来就是不合适的。

我们这里只讨论组件和元素的这层关系,vue 中就有了v-model来实现双向绑定,常见的应用常见就是 inputvalue 和某个变量的绑定。

如果没有 v-model,我们想实现这个 input value状态变量 的同步,那我们要在给 dom 加一个 oninput, 在value变化时同步更新状态变量,同时,在设置状态变量时,也要紧接着同步更新 value 值。

这是一个繁琐且重复的过程,所以 svelte 也提供了这种双向绑定的能力。  name 值的改变会更新输入值,输入值的改变也会更新 name

svelte
## 文本
<input bind:value={name}>

## 数字,内置了string 与 number转换的处理
<input type="number" bind:value={a} min="0" max="10" />
<input type="range" bind:value={a} min="0" max="10" />

## 复选框
<input type="checkbox" bind:checked={yes}>


## select
<select
	bind:value={selected}
	onchange={() => answer = ''}
>

class 和 style

和 JSX 一样,{} 内的都会被当 JS 处理

class="card {flipped ? 'flipped' : ''}"class={["card", { flipped }]}

style="transform: {flipped ? 'rotateY(0)' : ''}; --bg-1: palegoldenrod; --bg-2: black; --bg-3: goldenrod"

特殊的,style:

style:transform={flipped ? 'rotateY(0)' : ''} 
style:--bg-1="palegoldenrod" 
style:--bg-2="black" 
style:--bg-3="goldenrod"

actions

Action 本质上是元素级别的生命周期函数。

action 可以拓展元素的一些行为,官网里写了一个 trapFocus,当按下 tab 键时,让元素自动获取焦点。

js
export function trapFocus(node) {
	// node 是绑定的dom元素
	const previous = document.activeElement;

	function focusable() {}

	function handleKeydown(event) {}

	$effect(() => {
		focusable()[0]?.focus();
		node.addEventListener('keydown', handleKeydown);
		// 清除回调,释放资源
		return () => {
			node.removeEventListener('keydown', handleKeydown);
			previous?.focus();
		};
	});
}

在使用时:

svelte
<div class="menu" use:trapFocus>

案例二,接受内容生产函数的tooltip action

js
function tooltip(node, fn) {
	$effect(() => {
		const tooltip = tippy(node, fn());
		return tooltip.destroy;
	});
}
svelte
<button use:tooltip={() => ({ "😄" })}> Hover me </button>