Skip to content

Svelte初学

Svelte(发音:[svelt])是一个类似于 React 和 Vue 的前端框架,作者是 Rich Harris,同时也是 Rollup 的作者。

编译器而非框架(无运行时)

Svelte 是一种全新的构建用户界面的方法。传统框架如 React 和 Vue 在浏览器中需要做大量的工作,而 Svelte 将这些工作放到构建应用程序的编译阶段来处理。 与使用虚拟(virtual)DOM 差异对比不同。Svelte 编写的代码在应用程序的状态更改时就能像做外科手术一样更新 DOM。 我们知道 React 和 Vue ,都是基于运行时(runtime)的框架,应用都必须引入框架本身 —— 也就是运行时代码,这些运行时代码被用于构建虚拟 DOM、处理 diff 算法等等。并且,这些工作都是在浏览器中完成的,当用户在页面上进行各种操作改变组件的状态时,框架的 runtime 会根据新的组件状态(state)计算(diff)出哪些 DOM 节点需要被更新,从而更新视图。 而 Svelte 不同,它会将我们所写的 Svelte 组件(Svelte 也是组件化的思想)编译成与 DOM 直接交互的原生 JavaScript 代码,这是在构建过程中完成的,代码在运行时不需要再多加一个中间层,也就是说不需要额外引入一个所谓的框架运行时。所以说 Svelte 是编译器而非框架。

优势

  • 减少代码量
  • 无虚拟 DOM
  • 真正的反应能力
  • 打包体积更小

声明式添加数据

在组件<script> 标签声明一个 name 变量,然后在标签中应用name变量:

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

<h1>Hello {name}!</h1>

动态属性

跟上面控制文本一样,也可以使用花括号控制元素属性。

<script>
    let imageSrc = 'image.jpg';
</script>

<img src={imageSrc} alt="Super Man">

如果变量名与属性名相同,可以省去属性名,否则会报错

<script>
    let src = 'image.jpg';
</script>

// <img src={src} alt="Super Man"> //A11y: <img> element should have an alt attribute
<img {src} alt="Super Man">

同样属性内部也可以添加变量

<script>
    let src = 'image.jpg';
    let name = 'You';
</script>

<img {src} alt="Super Man is { name }">

css样式

组件的css样式规则的作用域会被限定在当前组件中,其原理是往标签添加一个class类,类似css scoped。

<style>
#header{
    color: green;
}
.main {
    width: 50px;
    height: 50px;
    background: yellow;
}
p{
    color: red
}
</style>

<span id="header">Header</span>
<div class="main"></div>
<p>This is a paragraph.</p>

最终css转化成:

#header.svelte-1vottuu{color:green}
.main.svelte-1vottuu{width:50px;height:50px;background:yellow}
p.svelte-1vottuu{color:red}

Reactivity 数据响应

赋值 Svelete 内核是一个强大的 (反应性)reactivity 系统,能够让 DOM 与你的应用程序状态保持同步。

<script>
let count = 0;

function handleClick() {
    count +=1
}
</script>

<button on:click={handleClick}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

响应式声明 - 值

类似Vue的computed,需要通过其他组件状态计算得到。

<script>
let count = 0;
$: doubled = count * 2;
</script>

<p>{count} doubled is {doubled}</p>

响应式声明 - 语句

类型Vue的watch, 监听某个变量的变化

$: console.log(`the count is ${count}`);

$: {
    console.log(`the count is ${count}`);
    alert(`I SAID THE COUNT IS ${count}`);
}

$: if (count >= 10) {
    alert(`count is dangerously high!`);
    count = 9;
}

更新数组和对象

由于 Svelte 的反应性是由赋值语句触发的,因此使用数组的诸如 push 和 splice 之类的方法就不会触发自动更新。 如下操作不会触发任何变化:

function addNumber() {
    numbers.push(numbers.length + 1);
}

解决该问题的一种方法是添加一个多余的赋值语句:

function addNumber() {
    numbers.push(numbers.length + 1);
    numbers = numbers;
}
// OR
function addNumber() {
	numbers = [...numbers, numbers.length + 1];
}

属性

属性定义 组件通过export声明props, 也可直接给props赋值

// Child.svelte
<script>
    export let answer;
    export let name = "Tony"
</script>

// App.svelte
<script>
import Child from './Child.svelte';
</script>
<Child amswer="a mystery" name="Judy" />

如果组件中含有一个对象属性,可以利用...语法将它们传到子组件中,而不用逐一指定。

<Info {...pkg}/>

逻辑

条件逻辑

通过把html代码放到if块中来控制渲染逻辑

{#if user.loggedIn}
    <button on:click={toggle}>
            Log out
    </button>
{/if}

{#if !user.loggedIn}
    <button on:click={toggle}>
        Log in
    </button>
{/if}

也可以直接写成 if ... else ...

{#if user.loggedIn}
    <button on:click={toggle}>
        Log out
    </button>
{:else}
    <button on:click={toggle}>
        Log in
    </button>
{/if}

也可以直接写成 if ... else if ...

{#if x > 10}
    <p>{x} is greater than 10</p>
{:else if 5 > x}
    <p>{x} is less than 5</p>
{:else}
    <p>{x} is between 5 and 10</p>
{/if}
循环逻辑

遍历数据列表使用each块

<ul>
    {#each cats as cat}
        <li><a target="_blank" href="https://www.baidu.com/s?ie=UTF-8&wd={cat.id}">
            {cat.name}
        </a></li>
    {/each}
</ul>

表达式 cats是一个数组,遇到数组或类似于数组的对象 (即具有length 属性)。你都可以通过 each [...iterable]遍历迭代该对象。

你也可以将index 作为第二个参数(key),类似于:

{#each cats as cat, i}
    <li><a target="_blank" href="https://www.baidu.com/s?ie=UTF-8&wd={cat.id}">
        {i + 1}: {cat.name}
    </a></li>
{/each}

如果你希望代码更加健壮,你可以使用each cats as { id, name }来解构,用cat.id 和 cat.name 来代替 id 和 name。

{#each cats as cat, i}
    <li><a target="_blank" href="https://www.baidu.com/s?ie=UTF-8&wd={cat.id}">
        {i + 1}: {cat.name}
    </a></li>
{/each}

each块添加key值

你可以将任何对象用作 key 来使用,就像Svelte 用 Map 在内部作为key一样,换句话说,你可以用 (thing) 来代替 (thing.id)作为 key 值。但是,使用字符串或者数字作为 key 值通常更安全,因为这能确保它的唯一性。

{#each things as thing (thing.id)}
	<Thing current={thing.color}/>
{/each}
事件
通过在标签添加on:event来添加事件
<div on:mousemove={handleMousemove}>
    The mouse position is {m.x} x {m.y}
</div>

添加内联函数 在某些框架中,出于性能原因,您可能会看到避免内联事件处理程序的建议,特别是在循环内部。这个建议不适用于Svelte——无论您选择哪种形式,编译器都会正常运行。

<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
    The mouse position is {m.x} x {m.y}
</div>

Await块

很多Web应用程序都可能在某个时候有需要处理异步数据的需求。使用 Svelte 在标签中使用 await 处理数据也是十分容易:

{#await promise}
	<p>...waiting</p>
{:then number}
	<p>The number is {number}</p>
{:catch error}
	<p style="color: red">{error.message}</p>
{/await}

如果无需知道 promise 返回错误,则可以忽略 catch 块。如果在请求完成之前不想程序执行任何操作,也可以忽略第一个块。

{#await promise then value}
	<p>the value is {value}</p>
{/await}
事件修饰符

DOM 事件处理程序具有额外的 修饰符(modifiers)。例如,带 once 修饰符的事件处理程序只运用一次:

<script>
    function handleClick() {
        alert('no more alerts')
    }
</script>

<button on:click|once={handleClick}>
    Click me
</button>

所有修饰符列表:

  • preventDefault :调用event.preventDefault() ,在运行处理程序之前调用。比如,对客户端表单处理有用。
  • stopPropagation :调用 event.stopPropagation(), 防止事件影响到下一个元素。
  • passive : 优化了对 touch/wheel 事件的滚动表现(Svelte 会在合适的地方自动添加滚动条)。
  • capture — 在 capture 阶段而不是bubbling 阶段触发事件处理程序 (MDN docs)
  • once :运行一次事件处理程序后将其删除。
  • self — 仅当 event.target 是其本身时才执行。

你可以将修饰符组合在一起使用,例如:on:click|once|capture={...}。

组件事件

组件也可以调度事件,为此,组件内必须创建一个相同事件并在外部进行分配。

// Inner.svelte
<script>
    // setup code goes here
    import { createEventDispatcher } from 'svelte';

    const dispatch = createEventDispatcher()

    function sayHello() {
        dispatch('message',{
            text: 'Hello123!'
        });
    }
</script>

<button on:click={sayHello}>
	Click to say hello
</button>

// App.svelte
<script>
    import Inner from './Inner.svelte';

    function handleMessage(event) {
        alert(event.detail.text);
    }
</script>

<Inner on:message={handleMessage}/>

createEventDispatcher 必须在首次实例化组件时调用它,—组件本身不支持如 setTimeout 之类的事件回调。 定义一个dispatch进行连接,进而把组件实例化。

生命周期

  • onMount
  • onDestroy
  • beforeUpdate
  • afterUpdate
  • tick

Vite构建Svelte

Vite也支持Svelte,用Vite开发可以使开发效率又可以提升一倍。 项目构建目录如下:

|-- .vscode |---- extensions.json |-- node_modules |-- public |---- vite.svg |-- src |---- assets |------- svelte.svg |---- lib |------- Counter.svelte |---- app.css |---- app.svelte |---- main.js |---- vite-env.d.ts --.gitignore -- index.html -- jsconfig.json -- package-lock.json -- package.json -- README.md -- vite.config.js

可安装@sveltejs/vite-plugin-svelte包,vite.config.js配置如下:

import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

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

本案例简单介绍Svelte一些基础知识,与详细了解,可查看其官方文档。