svelte框架的学习

481 阅读9分钟

svelte框架的学习

什么是Svelte?

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.jsReact.jsVue.js
应用性能比React和Vue更快比Svelte慢,比Vue略慢比Svelte慢,但比React略快
构建脚本编译器DOMVirtual DOM
平均应用大小15 Kb193 Kb71 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。

1.创建一个Svelte的程序

npm create vite@latest

使用vite创建项目有着更好的启动速度及更小的编译文件

1.数据渲染

<script>
  let name = "小明";
  let src = "./vite.svg";
</script>

<div>
//将变量放进一对花括号内,即可进行渲染,花括号内可以放表达式,即
  hello
  {name.replace(/([\W])(\W)/, "$1_$2")}
//甚至还可以对标签的属性进行简写,例如对img标签src属性进行简写
  <img {src} alt="" />
</div>

2、双向绑定

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

<script>
	let name = 'world';
</script>

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

checkbox双向绑定:

<script>
	let yes = false;
</script>

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

复选框组:

<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首‘}>

单选组:

<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:

<select value={selected} on:change="{() => answer = ''}">
	{#each questions as question}
		<option value={question}>
			{question.text}
		</option>
	{/each}
</select>

3、样式渲染

<script>
  import "./mystyle.css";
</script>

<div style="color: red">字符串</div>

<div class="myclass">蓝色文字</div>

<div class="yellowclass">黄色文字</div>

<div class="myclass2">大文字</div>

<!-- <style>
  .myclass {
    color: blue;
  }
</style> -->
<style lang="scss" type="text/scss">
  .myclass2 {
    font-size: 40px;
  }
</style>

按条件应用类名:

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

简写:

<div class:big>
	big类名
</div>

使得项目支持scss语法。

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

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

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

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

最终修改如下:

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属性写上。

<div class="bluetext">
  蓝色字符串,
  <span class="violettext">紫色字符串</span>
</div>

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

4.创建组件

在svelte项目中,每一个svelte为后缀的文件都是一个组件。(和react十分像)

匿名插槽

父组件:

<script>
	import Box from './Box.svelte';
</script>

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

子组件:

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

具名插槽

父组件:

<ContactCard>
	<span slot="name">
		P. Sherman
	</span>

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

子组件:

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

作用域插槽:

<Hoverable let:hovering={active}>
	{active}
</Hoverable>
<script>
	let hovering;
</script>

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

5、父传子

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

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

父组件:

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

子组件:

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

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

子组件:

<script>
	export let name;
	export let age;
</script>

父组件:

<script>
	let userinfo = {
		name: "小明",
		age: 18
	}
</script>

<Child {...userinfo} />

6、子传父

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

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

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

子组件:

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

父子组件传递与vue、react雷同

7、关于渲染html字符串

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

<script>
  let h5 = `我的名字叫:<span style="color: blue">小明</span>`;
</script>

{@html h5}

8、svelte事件

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

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

修饰符:

preventDefault — 停止默认事件修饰符

stopPropagation—停止冒泡修饰符

passive— 提高滚动性能

nonpassive—的设置passive: false

capture—捕获阶段处理事件

once— 只执行一次,完了后移除事件,使得下次不能被执行。

self— 仅在事件对象event.target为元素本身时执行事件。

trusted — 只有 event.isTrusted是 rue才进行触发。

例子:

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

例子:

<script>
  let count = 0;

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

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

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

9、svelte中的反应性

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

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

例子:

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

数量: - {count} +
价格:{price} 合计:{total}

10、修改数组或对象

在开发过程中经常会遇到一个问题,就是虽然修改了数组,但是不会产生效果的情况。是由于数组和对象变量的指向地址并无发生变化,使得sevelte不能识别是否发生的变量,无法进一步的触发渲染事件。可以通过浅拷贝或深拷贝的形式,使得变量所指向的地址发生改变即可。

<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}结束。

<script>
  let flag = true;
</script>

{#if flag}
  <div>真的</div>
{:else}
  <div>假的</div>
{/if}。
<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有对于循环也是有响应的模板语法。

{#each 数组 as 数组项目, 数组下标 (唯一的键值)}
	<div>{数组项目.属性}</div>
{/each}
<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}

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

<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配合使用的模板标签有等待、成功、失败状态(较为常用)

{#await Promise}
  等待状态
{:then 成功值}
  成功状态
{/await}

不含失败和等待状态

{#await Promise then 成功值}
  成功状态
{/await}
<script>
  let timer = new Promise((resolve) => {
    setTimeout(() => {
      resolve("倒计时完成");
    }, 3000);
  });
</script>

{#await timer}
  loading...
{:then r}
  {r}
{/await}
<script>
  let timer = new Promise((resolve) => {
    setTimeout(() => {
      resolve("倒计时完成");
    }, 3000);
  });
</script>

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

13、绑定元素

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

<script>
	let input;

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

<input bind:this={input} />