Svelte-基础篇
如 React、Vue 等框架是在运行时通过虚拟 DOM 进行差异对比,然后更新真实 DOM。
而 Svelte 的核心思想是在编译时就把组件代码转换成高效、命令式的 JavaScript 代码,直接操作真实 DOM。
基础
大概过了一下 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
{#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
<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 代码块
{#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
<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来实现双向绑定,常见的应用常见就是 input 的 value 和某个变量的绑定。
如果没有 v-model,我们想实现这个 input value 和 状态变量 的同步,那我们要在给 dom 加一个 oninput, 在value变化时同步更新状态变量,同时,在设置状态变量时,也要紧接着同步更新 value 值。
这是一个繁琐且重复的过程,所以 svelte 也提供了这种双向绑定的能力。 name 值的改变会更新输入值,输入值的改变也会更新 name。
## 文本
<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 键时,让元素自动获取焦点。
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();
};
});
}在使用时:
<div class="menu" use:trapFocus>案例二,接受内容生产函数的tooltip action
function tooltip(node, fn) {
$effect(() => {
const tooltip = tippy(node, fn());
return tooltip.destroy;
});
}<button use:tooltip={() => ({ "😄" })}> Hover me </button>