Svelte框架基础
2024-09-11 16:03:49

1、什么是Svelte

不知道大家曾几何时在学习或使用一个库或框架的时候,会觉得十分惊艳,并且想着要马上用于实际的项目中。如果有,那这个框架将会是Svelte.js。

在过去的一年里,Svelte一直在掀起波澜。事实上,根据Stack overflow的调查,它的欢迎程度逐渐飙升到第一位。

1663143640279

Svelte 是一个令人兴奋的 Web 框架,它为开发者构建 Web 应用程序提供了全新的视角。如果你已经拥有 React、Vue、Angular 或其他前端框架的开发经验,你可能会对 Svelte 感到惊喜。

在本教程中,我们将介绍:

  • 什么是Svelte?
  • Svelte与其它框架的区别
  • 什么时候应该使用Svelte
  • 利用 Svelte 创建待办事项列表应用 TODOLIST

Svelte.js标志

Svelte.js是一个开源的JavaScript框架,通过将Svelte代码转换为流畅的UI界面,简化了Web应用程序的创建。该框架的一个关键区别是:它在编译阶段加载框架而不是用户运行时加载,因此其比 React 或 Vue 更快。

Svelte由它的架构决定运行速度。该框架将代码编译成独立的小型JavaScript模块,确保浏览器尽可能少地完成工作,从而加快加载速度。

Svelte 超快的速度背后有三个主要原因:

  • 无虚拟DOM: Svelte在没有虚拟DOM的情况下实现了与 React 和 Vue 相同的功能。这意味着您可以在不依赖虚拟元素的情况下使用它,并获得更好的性能优势。换句话说,Svelte 在没有 DOM 的情况下直接处理代码,并将大部分代码的处理放在编译阶段去完成。
  • 减少代码量:Svelte 将你的代码编译成体积小、不依赖框架的普通 JS 代码,让你的应用程序无论启动还是运行都变得迅速。
  • 响应式能力:Svelte和 React一样,对数据变化做出自己的反应,它不需要浏览器做额外的工作来将组件转换为DOM操作,将数据更改呈现为JavaScript代码。

如果你掌握了JS、HTML、CSS,则学习Svelte的时候,无任何明显的学习曲线。它允许 Web 开发人员构建各种规模的 Web 应用程序,同时为访客提供可靠的用户体验和超高的性能。

Svelte与其它框架的区别:Svelte、React、Vue 的对比

Svelte与 React、Vue等等的框架对比,Svelte构建的应用程序是事先编译的,因此不必将整个框架提供给每个网站访问者。因此,用户的体验更流畅,消耗更少的带宽,这一切都感觉更快,更轻量级。

这是一个对照的图表,您可以一目了然地查看这三个框架之间的差异。

Svelte.js React.js Vue.js
应用性能 比React和Vue更快 比Svelte慢,比Vue略慢 比Svelte慢,但比React略快
构建 脚本编译器 DOM Virtual DOM
平均应用大小 15 Kb 193 Kb 71 Kb
学习曲线 简单易学 相对容易学习 相对容易学习

简单来说,Svelte可以让您提高效率。通过让您使用您熟悉的语言和符号(HTML、CSS、JavaScript),即使是初学者也可以快速入门。另一方面,React 和 Vue 需要类型脚本和 JSX 技能。

除此之外,Svelte 不依赖于在运行时加载的复杂库。相反,它会编译你的代码,并加载一个比 React更小的软件包。这种体积的差异换来的是访客更快的加载时间。

与 Vue 和 React 不同,Svelte几乎不需要初始化的脚手架,因为它是使用基本的 HTML、CSS 和 JavaScript 编写的。所以,Svelte的脚本看起来类似于普通的JS。

使用Svelte.js的好处

现在我们上面表格的对比研究了Svelte与其替代方案,让我们来谈谈Svelte能成为最受欢迎的框架的原因:

  • 更好的开发体验:Svelte是最受欢迎的框架,因为它设置更加简单,让开发人员可以更专注写的业务代码。
  • 模块化 CSS:默认情况下,Svelte样式的有自己的作用域,这意味着当您将Svelte文件编译时,编译器将为每个元素生成唯一的类名。这确保了不同页面展示出来的效果是独立的。
  • 内置动画: 在Svelte 中使用动画是一种很好的体验。它内置了强大而令人愉悦的交互功能,无需额外的软件包。
  • 更小的体积: 编译完成后的代码,拥有更小的体积,更快的加载速度。

什么时候应该使用Svelte.js?

坦率地说,这一切都归结为你打算用它来构建具体的项目。仅仅因为它提供了快速的性能,并不能使它成为解决您所有问题。通常,我建议在以下情况下使用 Svelte:

  • 构建快速、响应迅速的网站:Svelte 的小捆绑包尺寸确保您创建的任何内容都能快速运行。这使得它非常适合那些想要快速,SEO驱动的网站和卓越的网络体验的客户。
  • 为连接性较差的设备创建 Web 应用程序:由于 Svelte 使用的代码更少,这意味着要下载和执行的 字节数 更少,因此非常适合构建适用于 网络 或 设备性能 较差的 应用程序。
  • 设计交互式页面:动画和过渡内置于 Svelte 中。开发人员可以使用svelte/animate模块创建交互式内容,这是让访问者与网站保持互动的好方法,而且并不会影响加载速度和SEO。

Svelte.js有很大的学习曲线吗?

Svelte是新手编码初学者的完美平台。只需一个HTML / CSS和JavaScript技能组合,您就可以从头开始构建您的第一个网站,而无需额外的知识。

这使得学习曲线非常小,不像它的大多数替代方案。除此之外,Svelte 还提供可靠的学习资源,确保您在短短几天内掌握细节,而不是像 React、Vue 或 Angular 那样的数周或数月。

Svelte.js入门

到现在为止,我相信您已经想蠢蠢欲动,并开始写Svelte代码了。

为此,我准备了一个初学者教程,向您展示如何使用 Svelte 组件创建待办事项列表。我们将构建一个交互式表单,您可以在其中将待办事项标记为已完成,也可以将不想要的待办事项删除。

在开始编写这个《待办事项列表》应用前,我们先来学习一下Svelte的基本语法。

假设,我不是一个做事有条理的人,需要建立一个待办事项程序,并希望这个待办事项程序能帮助逐一完成所有的事情。

所有大家和我一起,踏上这个Svelte开发之旅,让我们一起构建这个应用程序。

先决条件:

这是您开始学习Svelte所需的内容:

!!!小提示:git bash终端中,有时候不能正常使用vite脚手架键入文字。(右键git bash here打开的终端)

可以通过增加一下内容,使得vite脚手架正常使用。

1
2
nano ~/.bashrc
export MSYS="enable_pcon"

创建第一个svelte程序:

1669913783920

目录结构:

1669914023121

编写第一个程序:Hello world

1669914101696

结论

使用 Svelte 后,我明白了为什么开发人员喜欢它。这是一个有前途的编译器(或框架),虽然不是每个项目都完美,但可以帮助你快速和交互式地构建一些东西。
它比其他工具更不复杂,使您能够在几分钟内开始运行。此外,如果您遇到障碍,您将在互联网上找到大量资源来指导您,并且社区对初学者很友好。
如果你想学习一个新的框架,或者想尝试一些不同的东西,Svelte绝对值得仔细研究。
你已经用过Svelte了吗?请告诉我你对它的看法。

2、svelte基本语法

1、数据渲染

1669914383577

将变量放进一对花括号内,即可进行渲染,花括号内可以放表达式,即:

1669914481793

甚至还可以对标签的属性进行简写,例如对img标签src属性进行简写:

1669915130919

2、双向绑定

双向绑定只需要通过 bind:value 即可完成。若单选框组、复选框组:还需要添加bind:group属性和value值。

1
2
3
4
5
6
<script>
let name = 'world';
</script>

<input bind:value={name}>
<h1>Hello {name}!</h1>

checkbox双向绑定:

1
2
3
4
5
6
7
<script>
let yes = false;
</script>

<label>
<input type="checkbox" checked={yes}>
</label>

复选框组:

1
2
3
<input type=checkbox bind:group={books} name="books" value={‘钢铁‘}>
<input type=checkbox bind:group={books} name="books" value={‘卖火柴‘}>
<input type=checkbox bind:group={books} name="books" value={‘唐诗300首‘}>

单选组:

1
2
3
<input type=radio bind:group={books} name="books" value={‘钢铁‘}>
<input type=radio bind:group={books} name="books" value={‘卖火柴‘}>
<input type=radio bind:group={books} name="books" value={‘唐诗300首‘}>

select:

1
2
3
4
5
6
7
<select value={selected} on:change="{() => answer = ''}">
{#each questions as question}
<option value={question}>
{question.text}
</option>
{/each}
</select>

3、样式渲染

行内样式:

1669915327950

1
2
3
4
<p 
style:color=‘red’
style:--opacity="{bgOpacity}"
>this is text</p>

样式表:

1669915400161

按条件应用类名:

1
<button	class:selected="{current === 'foo'}">clickme</button>

简写:

1
2
3
<div class:big>
big类名
</div>

使得项目支持scss语法。

想要使得项目支持scss,样式预处理语言。

需要先安装预处理器:svelte-preprocess, 由于需要支持scss,那sass当然也需要进行安装。

npm install svelte-preprocess node-sass --save-dev

安装好预处理器后,还需要对脚手架配置文件vite.config.js进行修改:

最终修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import sveltePreprocess from "svelte-preprocess";

// https://vitejs.dev/config/
export default defineConfig({
plugins: [
svelte({
preprocess: sveltePreprocess(),
}),
],
});

将scss写在style即可。不要忘记,将lang和type属性写上。

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="bluetext">
蓝色字符串,
<span class="violettext">紫色字符串</span>
</div>

<style lang="scss" type="text/scss">
.bluetext {
color: blue;
.violettext {
color: violet;
}
}
</style>

1669916529314

4、创建组件

在svelte项目中,每一个svelte为后缀的文件都是一个组件。

导入组件的方式也十分方便,使用import进行导入即可。千万不要忘记加svelte文件后缀。

1669917435144

4.1、匿名插槽

父组件:

1
2
3
4
5
6
7
8
<script>
import Box from './Box.svelte';
</script>

<Box>
<h2>Hello!</h2>
<p>This is a box. It can contain anything.</p>
</Box>

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="box">
<slot></slot>
</div>

<style>
.box {
width: 300px;
border: 1px solid #aaa;
border-radius: 2px;
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
padding: 1em;
margin: 0 0 1em 0;
}
</style>

4.2、具名插槽

父组件:

1
2
3
4
5
6
7
8
9
10
<ContactCard>
<span slot="name">
P. Sherman
</span>

<span slot="address">
42 Wallaby Way<br>
Sydney
</span>
</ContactCard>

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<article class="contact-card">
<h2>
<slot name="name">
<span class="missing">Unknown name</span>
</slot>
</h2>

<div class="address">
<slot name="address">
<span class="missing">Unknown address</span>
</slot>
</div>

<div class="email">
<slot name="email">
<span class="missing">Unknown email</span>
</slot>
</div>
</article>

作用域插槽:

1
2
3
<Hoverable let:hovering={active}>
{active}
</Hoverable>
1
2
3
4
5
6
7
<script>
let hovering;
</script>

<div on:mouseenter={() => hovering = true} on:mouseleave={() => hovering = false}>
<slot hovering={hovering}></slot>
</div>

5、父传子

父传子的过程需要子组件暴露属性,父组件才能进行参数的传递。

如何暴露属性? 子组件可以在定义响应式变量的过程中,在前面加上export关键词,即可定义参数属性。

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
import Child from "./Child.svelte";
</script>

<div class="c-parent">
我是父组件
<Child text="你好子组件" />
</div>

<style lang="scss" type="text/scss">
.c-parent {
width: 500px;
height: 500px;
background-color: rgb(29, 210, 255);
}
</style>

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
export let text = ""; // 此处暴露出去的值,可以赋值默认值。
</script>

<div class="c-child">
我是子组件
<br />
父组件信息:{text}
</div>

<style lang="scss" type="text/scss">
.c-child {
width: 300px;
height: 300px;
background-color: rgb(166, 39, 245);
}
</style>

最终效果:

1669919534295

若子组件定义多个属性,父组件还可以通过解构的方式去传递参数:

子组件:

1
2
3
4
<script>
export let name;
export let age;
</script>

父组件:

1
2
3
4
5
6
7
8
<script>
let userinfo = {
name: "小明",
age: 18
}
</script>

<Child {...userinfo} />

6、子传父

在svelte中提供了一个创建事件调度器的方法createEventDispatcher来创建事件调度方法,开发者可以利用该事件调度方法来调度事件。从而达到子传父的目的。

父组件:在父组件中监听自定义方法on:hello,当子组件调度hello事件的时候,父组件能接收到传递过来的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
import Child from "./Child.svelte";
</script>

<div class="c-parent">
我是父组件
<Child
text="你好子组件"
on:hello={(e) => {
console.log("父组件收到的信息:", e.detail);
}}
/>
</div>

<style lang="scss" type="text/scss">
.c-parent {
width: 500px;
height: 500px;
background-color: rgb(29, 210, 255);
}
</style>

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
const sendToParent = () => {
dispatch("hello", "this is message");
};
export let text = "";
</script>

<div class="c-child">
我是子组件
<br />
父组件信息:{text}
<br />
<button on:click={sendToParent}>传递到父组件</button>
</div>

<style lang="scss" type="text/scss">
.c-child {
width: 300px;
height: 300px;
background-color: rgb(166, 39, 245);
}
</style>

1669921556237

上下文传参:

定义:

1
2
3
4
5
6
7
<script>
import { setContext } from 'svelte';
setContext('mykey', {
a: "aaaaaaaaaaaaaa",
b: "bbbbbbbbbbbbbb"
});
</script>

使用:

1
2
3
4
<script>
import { getContext } from 'svelte';
const { a, b } = getContext('mykey');
</script>

7、关于渲染html字符串

在svelte中提供一个特殊的标记 @html,使用该标记可以为我们渲染html字符串。

1669917721584

8、svelte事件

在svelte中定义事件也十分简单,与原生类似,不同的是,需要在on后面加上冒号。

格式如:on:事件名={方法引用}

修饰符:

  • preventDefault — 停止默认事件修饰符
  • stopPropagation —停止冒泡修饰符
  • passive — 提高滚动性能
  • nonpassive — 显式的设置passive: false
  • capture —捕获阶段处理事件
  • once — 只执行一次,完了后移除事件,使得下次不能被执行。
  • self — 仅在事件对象event.target为元素本身时执行事件。
  • trusted — 只有 event.isTrustedtrue才进行触发。

例子:

1
<button on:click|once={事件函数}>点击我</button>

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
let count = 0;

const reduce = () => {
count--;
};

const add = () => {
count++;
};
</script>

<div>
数量:
<button on:click={reduce}>-</button>
{count}
<button on:click={add}>+</button>
</div>

效果如下:

1669917932947

9、svelte中的反应性

从第一小节可得知,开发者只需要定义一个变量,则该变量就是响应式。

在svelte中,提供一个反应性的语法,在script标签中用$:符合进行定义。先来理解什么是反应性,当被依赖的响应式变量发生改变的时候,会自动同步更新反应性语法里面的表达式。

例子:

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

const reduce = () => {
count--;
};

const add = () => {
count++;
};

$: console.log("count变成:%d", count);
</script>

<div>
数量:
<button on:click={reduce}>-</button>
{count}
<button on:click={add}>+</button>
</div>

效果如下:

1669918526019

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
let count = 0;

const reduce = () => {
count--;
};

const add = () => {
count++;
};

let price = 16.8;

$: console.log("count变成:%d", count);
$: total = (count * price).toFixed(2);
</script>

<div>
数量:
<button on:click={reduce}>-</button>
{count}
<button on:click={add}>+</button>
<br />
价格:{price} 合计:{total}
</div>

效果如下:

1669918651537

10、修改数组或对象

在开发过程中经常会遇到一个问题,就是虽然修改了数组,但是不会产生效果的情况。

为什么会产生这样的问题呢?原因是由于数组和对象变量的指向地址并无发生变化,使得sevelte不能识别是否发生的变量,无法进一步的触发渲染事件。

如何解决该问题?可以通过浅拷贝或深拷贝的形式,使得变量所指向的地址发生改变即可。

通常做法有:

对象:

1、Object.assign({}, obj1, obj2)

2、{…obj1, …obj2}

3、JSON.parse(JSON.stringify(obj1))

数组:

1、[…arr1, …arr2]

2、JSON.parse(JSON.stringify(arr1))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
let arr = [1, 2, 3];

$: total = arr.reduce((total, val) => (total += val));
</script>

<div>
{arr.join(" + ")} = {total}

<br />
<button
on:click={() => {
arr.push(arr.length + 1);
arr = [...arr];
}}>add item</button
>
</div>

11、条件渲染

svelte有着自己的一套模板语法,使用起来结构更加清晰.

可以看到如下代码,条件渲染的条件是放在标签语法{#if}里面,而分支用 {:else} 分开,最终再以{/if}结束。

1
2
3
4
5
6
7
8
9
<script>
let flag = true;
</script>

{#if flag}
<div>真的</div>
{:else}
<div>假的</div>
{/if}

结果:真的

当然,条件渲染也支持嵌套。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
\<script\>
let flag = true;
let flag2 = false;
\<\/script\>

{#if flag}
<div>真的</div>
{#if flag2}
<div>真的</div>
{:else}
<div>假的</div>
{/if}
{:else}
<div>假的</div>
{/if}

结果:真的

假的

12、列表渲染

同样svelte有对于循环也是有响应的模板语法。

格式:

1
2
3
{#each 数组 as 数组项目, 数组下标 (唯一的键值)}
<div>{数组项目.属性}</div>
{/each}

示例1、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
import { each } from "svelte/internal";

let arr = [
{ name: "小明", age: 20 },
{ name: "小红", age: 19 },
{ name: "小蓝", age: 20 },
{ name: "小天", age: 15 },
];
</script>

{#each arr as item, index}
<div>
{index}、姓名:{item.name} 年龄:{item.age}
</div>
{/each}

示例2: 亦可以通过each后面的圆括号来指定唯一的键(key)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
import { each } from "svelte/internal";

let arr = [
{ id: 1, name: "小明", age: 20 },
{ id: 2, name: "小红", age: 19 },
{ id: 3, name: "小蓝", age: 20 },
{ id: 4, name: "小天", age: 15 },
];
</script>

{#each arr as item, index (item.id)}
<div>
{index}、姓名:{item.name} 年龄:{item.age}
</div>
{/each}

10、Await模板标签

svelte有个与promise配合使用的模板标签,以提高用户的体验感。

语法1:含等待、成功、失败状态(较为常用)

1
2
3
4
5
{#await Promise}
等待状态
{:then 成功值}
成功状态
{/await}

语法2:不含失败和等待状态

1
2
3
{#await Promise then 成功值}
成功状态
{/await}

例子1、

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
let timer = new Promise((resolve) => {
setTimeout(() => {
resolve("倒计时完成");
}, 3000);
});
</script>

{#await timer}
loading...
{:then r}
{r}
{/await}

例子2、

1
2
3
4
5
6
7
8
9
10
11
<script>
let timer = new Promise((resolve) => {
setTimeout(() => {
resolve("倒计时完成");
}, 3000);
});
</script>

{#await timer then r}
{r}
{/await}

13、绑定元素

可以通过bind:this,将元素绑定到具体的变量中去。

1
2
3
4
5
6
7
8
9
<script>
let input;

export function focus() {
input.focus();
}
</script>

<input bind:this={input} />

3、Svelte基础语法

1、生命周期

什么是生命周期?生命周期就像人的生老病死一样是具体的某个时间段。那么软件也是一样,有特定的具体时刻所触发的事件。

svelte中的生命周期:onMount、onDestroy、beforeUpdate、afterUpdate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
import { onMount, onDestroy, beforeUpdate, afterUpdate } from 'svelte';

// 挂载期
onMount(async () => {
// 一般可以通过挂载来进行网络请求。
});

// dom节点更新前
beforeUpdate(() => {
// 用于记录dom节点更新前的状态
})

// dom节点更新后
afterUpdate(() => {
// 在dom接口更新后所需事件
})

// 卸载期
onDestroy(() => {
// 卸载工作
// 例如释放变量、删除时钟。
});
</script>

2、tick的用法

tick函数返回一个Promise, 你可以在任意地方使用tick。它的作用是:有了await tick()后,它不会里面刷新dom,而去等待下一次微任务就绪的时候(包括其他组件已经渲染完成)再继续往下执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
import { tick } from "svelte";

let count = 0;
async function hello() {
count++;

new Promise((resolve) => {
setTimeout(() => {
resolve("hello");
}, 3000);
}).then((res) => {
console.log(res);
});

await tick();
console.log(count);
}
</script>

count: {count}
<button on:click={hello}>click me</button>

3、全局状态管理

store.svelte文件:

1
2
import { writable } from 'svelte/store';
export const count = writable(0);

App.svelte

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script>
import { count } from './stores.js';

// 减少
function decrement() {
count.update(n => n - 1);
}

// 增加
function increment() {
count.update(n => n + 1);
}

// 重置
function reset() {
count.set(0);
}

let countValue;
count.subscribe(value => {
countValue = value;
});
</script>

<button on:click={increment}>+</button>
{countValue}
简写:{$count}
<button on:click={decrement}>-</button>
<br />
<button on:click={reset}>重置</button>

只读全局仓库:只允许内部修改

1
2
3
4
5
6
7
8
9
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);

return function stop() {
clearInterval(interval);
};
});

利用derived基于原有的仓库数据进行定义新变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { readable, derived } from 'svelte/store';

export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);

return function stop() {
clearInterval(interval);
};
});

const start = new Date();

export const elapsed = derived(
time,
$time => Math.round(($time - start) / 1000)
);

4、tweened补间动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';

const progress = tweened(0, {
duration: 400,
easing: cubicOut
});
</script>

<progress value={$progress}></progress>
<button on:click="{() => progress.set(0)}">0%</button>
<button on:click="{() => progress.set(0.5)}">50%</button>
<button on:click="{() => progress.set(1)}">100%</button>

<style>
progress {
display: block;
width: 100%;
}
</style>

5、spring动画

spring是用于替代tweened适用于实时性更高的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<script>
import { spring } from 'svelte/motion';

let coords = spring({ x: 50, y: 50 }, {
stiffness: 0.1,
damping: 0.25
});

let size = spring(10);
</script>

<div style="position: absolute; right: 1em;">
<label>
<h3>stiffness ({coords.stiffness})</h3>
<input bind:value={coords.stiffness} type="range" min="0" max="1" step="0.01">
</label>

<label>
<h3>damping ({coords.damping})</h3>
<input bind:value={coords.damping} type="range" min="0" max="1" step="0.01">
</label>
</div>

<svg
on:mousemove="{e => coords.set({ x: e.clientX, y: e.clientY })}"
on:mousedown="{() => size.set(30)}"
on:mouseup="{() => size.set(10)}"
>
<circle cx={$coords.x} cy={$coords.y} r={$size}/>
</svg>

<style>
svg {
width: 100%;
height: 100%;
}
circle {
fill: #ff3e00;
}
</style>

6、显隐动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
import { fade, fly } from 'svelte/transition';
let visible = true;
</script>

<label>
<input type="checkbox" bind:checked={visible}>
visible
</label>

{#if visible}
<p transition:fade>
Fades in and out
</p>

<p transition:fly="{{ y: 200, duration: 2000 }}">
Flies in and out
</p>

<p in:fly="{{ y: 200, duration: 2000 }}" out:fade>
Flies in, fades out
</p>
{/if}

7、其他

window对象

1
<svelte:window on:keydown={handleKeydown}/>

body对象

1
2
3
4
<svelte:body
on:mouseenter={handleMouseenter}
on:mouseleave={handleMouseleave}
/>

Svelte项目实战

1、Axios封装

安装axios

npm install axios

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
export function fetch(uri, data, method = "POST", responseType = "json") {
return new Promise((resolve, reject) => {
axios({
url: BASEURL + uri,
method: method,
params: method === "GET" ? data : null,
data: method === "POST" ? data : null,
// headers: {
// "Content-Type": "application/json",
// },
responseType: responseType,
})
.then(function (response) {
resolve(response.data);
})
.catch(err => {
reject(err);
});
});
}

2、数据请求

创建api.js文件,导入fetch文件。

在api.js写请求接口。

3、页面制作

使用蓝湖还原设计图。

4、路由配置

安装路由依赖:

1
npm install svelte-spa-router

定义路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

import Home from './routes/Home.svelte'
import Author from './routes/Author.svelte'
import Book from './routes/Book.svelte'
import NotFound from './routes/NotFound.svelte'

const routes = {
// Exact path
'/': Home,

// Using named parameters, with last being optional
'/author/:first/:last?': Author,

// Wildcard parameter
'/book/*': Book,

// Catch-all
// This is optional, but if present it must be the last
'*': NotFound,
}

使用路由:

1
2
3
<body>
<Router {routes}/>
</body>

接收路由参数:

1
2
3
4
5
<script>
import {location, querystring} from 'svelte-spa-router'
</script>
<p>The current page is: {$location}</p>
<p>The querystring is: {$querystring}</p>

路由跳转:

声明式:

1
2
3
4
5
<script>
import {link} from 'svelte-spa-router'
let myLink = "/book/456"
</script>
<a use:link={myLink}>The Biggest Princess</a>

导航式:

1
2
3
4
5
6
7
8
9
10
11
import {push, pop, replace} from 'svelte-spa-router'

// The push(url) method navigates to another page, just like clicking on a link
push('/book/42')

// The pop() method is equivalent to hitting the back button in the browser
pop()

// The replace(url) method navigates to a new page, but without adding a new entry in the browser's history stack
// So, clicking on the back button in the browser would not lead to the page users were visiting before the call to replace()
replace('/book/3')