MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Svelte组件化开发入门:创建第一个Svelte组件

2022-11-015.3k 阅读

理解 Svelte 组件化开发的基础

在深入创建第一个 Svelte 组件之前,我们先来理解一下 Svelte 组件化开发的核心概念。Svelte 是一种用于构建用户界面的JavaScript框架,它与传统框架如 React、Vue 有所不同。传统框架通常在运行时通过虚拟 DOM 等机制来更新页面,而 Svelte 是在编译时将组件代码转换为高效的JavaScript代码,直接操作真实 DOM,从而获得更好的性能。

组件的定义与结构

Svelte 组件本质上是一个包含 HTML、CSS 和 JavaScript 的独立单元。每个 Svelte 组件通常存储在一个 .svelte 文件中,文件名通常与组件名相关。例如,一个名为 Button.svelte 的文件定义了一个按钮组件。

在 Svelte 组件中,HTML 部分定义了组件的结构,CSS 部分定义了组件的样式,而 JavaScript 部分则处理组件的逻辑,包括状态管理、事件处理等。

组件的作用域

Svelte 组件具有自己独立的作用域。这意味着组件内部定义的变量、函数等在组件外部是不可访问的,反之亦然。这种隔离性使得组件之间的交互更加可控,避免了全局变量带来的命名冲突等问题。

创建第一个 Svelte 组件

创建项目结构

首先,我们需要创建一个 Svelte 项目。可以使用 Svelte 官方提供的脚手架工具 degit 来快速搭建项目结构。

确保你已经安装了 Node.jsnpm(Node Package Manager)。然后在终端中运行以下命令:

npx degit sveltejs/template my - first - svelte - app
cd my - first - svelte - app
npm install

上述命令中,npx degit sveltejs/template my - first - svelte - app 会从 sveltejs/template 模板创建一个名为 my - first - svelte - app 的新项目。cd my - first - svelte - app 进入项目目录,npm install 安装项目所需的依赖。

创建一个简单的组件

在项目的 src 目录下,创建一个新的文件 MyComponent.svelte。这将是我们的第一个 Svelte 组件。

MyComponent.svelte 文件中,输入以下代码:

<script>
    let message = 'Hello, Svelte!';
</script>

<div>
    <p>{message}</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

在上述代码中:

  • <script> 部分:定义了一个名为 message 的变量,并初始化为 'Hello, Svelte!'。这个变量将用于在组件的 HTML 部分显示文本。
  • <div> 部分:这是组件的 HTML 结构。<p> 标签中使用 {message} 来插值显示 message 变量的值。
  • <style> 部分:定义了 div 元素的样式,包括背景颜色、内边距和边框半径。

在主应用中使用组件

在 Svelte 项目中,main.js 是应用的入口文件,而 App.svelte 是主组件。我们要在 App.svelte 中使用刚刚创建的 MyComponent.svelte 组件。

打开 App.svelte 文件,修改代码如下:

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

<main>
    <h1>My Svelte App</h1>
    <MyComponent />
</main>

<style>
    main {
        text - align: center;
        padding: 1em;
        max - width: 240px;
        margin: 0 auto;
    }
</style>

在上述代码中:

  • <script> 部分:使用 import 语句导入了 MyComponent.svelte 组件。
  • <main> 部分:在 main 元素中,通过 <MyComponent /> 标签使用了 MyComponent 组件。

运行项目

在项目目录下的终端中运行以下命令启动开发服务器:

npm run dev

然后在浏览器中访问 http://localhost:5000(默认端口为 5000,如果被占用会自动切换到其他端口),你将看到一个浅蓝色背景的区域,里面显示着 Hello, Svelte!,这就是我们创建并使用的第一个 Svelte 组件。

组件的属性(Props)

什么是属性

组件的属性(Props)是一种向组件传递数据的方式。通过属性,我们可以使组件更加灵活和可复用。例如,我们可以创建一个通用的 Button 组件,通过属性来设置按钮的文本、颜色等。

定义和使用属性

MyComponent.svelte 中,我们来添加一个属性。修改代码如下:

<script>
    export let title = 'Default Title';
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

在上述代码中,使用 export let 定义了一个名为 title 的属性,并设置了默认值 'Default Title'。在 HTML 部分,使用 {title} 来显示属性的值。

然后在 App.svelte 中使用这个带有属性的组件:

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

<main>
    <h1>My Svelte App</h1>
    <MyComponent title="Custom Title" />
</main>

<style>
    main {
        text - align: center;
        padding: 1em;
        max - width: 240px;
        margin: 0 auto;
    }
</style>

App.svelte 中,通过 title="Custom Title"MyComponent 组件传递了一个自定义的 title 属性值。

组件的事件处理

事件绑定基础

Svelte 提供了简洁的方式来处理组件内的事件,如点击、鼠标移动等。事件绑定使用 on: 前缀加上事件名的方式。

示例:按钮点击事件

MyComponent.svelte 中添加一个按钮,并处理按钮的点击事件。修改代码如下:

<script>
    export let title = 'Default Title';
    let clickCount = 0;

    const handleClick = () => {
        clickCount++;
    };
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
    <button on:click={handleClick}>Click me</button>
    <p>You clicked {clickCount} times</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

在上述代码中:

  • <script> 部分:定义了一个 clickCount 变量用于记录点击次数,以及一个 handleClick 函数,在函数中每次点击时将 clickCount 加 1。
  • <button> 部分:通过 on:click={handleClick} 将按钮的点击事件绑定到 handleClick 函数。
  • <p> 部分:显示当前的点击次数。

组件的生命周期

生命周期概述

组件的生命周期是指组件从创建到销毁过程中所经历的一系列阶段。Svelte 提供了一些生命周期函数,让我们可以在这些阶段执行特定的代码。

生命周期函数

  1. onMount:在组件第一次插入到 DOM 后调用。这是一个执行副作用操作(如 API 调用)的好时机。

MyComponent.svelte 中使用 onMount

<script>
    import { onMount } from'svelte';

    export let title = 'Default Title';
    let clickCount = 0;

    const handleClick = () => {
        clickCount++;
    };

    onMount(() => {
        console.log('Component has been mounted');
    });
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
    <button on:click={handleClick}>Click me</button>
    <p>You clicked {clickCount} times</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>
  1. beforeUpdate:在组件的状态发生变化,DOM 即将更新之前调用。
<script>
    import { beforeUpdate } from'svelte';

    export let title = 'Default Title';
    let clickCount = 0;

    const handleClick = () => {
        clickCount++;
    };

    beforeUpdate(() => {
        console.log('DOM is about to be updated');
    });
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
    <button on:click={handleClick}>Click me</button>
    <p>You clicked {clickCount} times</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>
  1. afterUpdate:在组件的状态发生变化,DOM 更新完成之后调用。
<script>
    import { afterUpdate } from'svelte';

    export let title = 'Default Title';
    let clickCount = 0;

    const handleClick = () => {
        clickCount++;
    };

    afterUpdate(() => {
        console.log('DOM has been updated');
    });
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
    <button on:click={handleClick}>Click me</button>
    <p>You clicked {clickCount} times</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>
  1. onDestroy:在组件从 DOM 中移除之前调用。这是清理副作用(如取消定时器、解绑事件监听器)的好时机。
<script>
    import { onDestroy } from'svelte';

    export let title = 'Default Title';
    let clickCount = 0;

    const handleClick = () => {
        clickCount++;
    };

    const intervalId = setInterval(() => {
        console.log('Interval is running');
    }, 1000);

    onDestroy(() => {
        clearInterval(intervalId);
        console.log('Component is being destroyed');
    });
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
    <button on:click={handleClick}>Click me</button>
    <p>You clicked {clickCount} times</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

组件的插槽(Slots)

插槽的概念

插槽是 Svelte 中一种强大的机制,它允许我们在组件中定义一个或多个占位符,然后在使用组件时插入自定义内容。这使得组件更加灵活和可定制。

单个插槽示例

MyComponent.svelte 中添加一个插槽。修改代码如下:

<script>
    export let title = 'Default Title';
</script>

<div>
    <h2>{title}</h2>
    <slot>Default slot content</slot>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

在上述代码中,<slot> 标签定义了一个插槽。如果在使用组件时没有传入内容,将显示 Default slot content

App.svelte 中使用带有插槽的组件:

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

<main>
    <h1>My Svelte App</h1>
    <MyComponent title="Custom Title">
        <p>This is custom content in the slot</p>
    </MyComponent>
</main>

<style>
    main {
        text - align: center;
        padding: 1em;
        max - width: 240px;
        margin: 0 auto;
    }
</style>

App.svelte 中,通过在 <MyComponent> 标签内插入 <p> 标签,将自定义内容插入到了插槽中。

具名插槽

具名插槽允许我们在组件中定义多个插槽,并通过名称来区分。

MyComponent.svelte 中定义具名插槽:

<script>
    export let title = 'Default Title';
</script>

<div>
    <h2>{title}</h2>
    <slot name="header">Default header slot content</slot>
    <slot name="body">Default body slot content</slot>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

App.svelte 中使用具名插槽:

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

<main>
    <h1>My Svelte App</h1>
    <MyComponent title="Custom Title">
        <p slot="header">This is custom header content</p>
        <p slot="body">This is custom body content</p>
    </MyComponent>
</main>

<style>
    main {
        text - align: center;
        padding: 1em;
        max - width: 240px;
        margin: 0 auto;
    }
</style>

在上述代码中,通过 slot="header"slot="body" 将不同的内容插入到对应的具名插槽中。

组件的状态管理

状态管理的重要性

随着应用规模的增长,组件之间的状态传递和共享变得复杂。良好的状态管理可以使应用的逻辑更加清晰,易于维护。

简单状态管理示例

在 Svelte 中,每个组件都可以有自己的状态。我们已经在 MyComponent.svelte 中看到了简单的状态管理,如 clickCount 变量。

<script>
    export let title = 'Default Title';
    let clickCount = 0;

    const handleClick = () => {
        clickCount++;
    };
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
    <button on:click={handleClick}>Click me</button>
    <p>You clicked {clickCount} times</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

这里的 clickCount 就是 MyComponent 组件的局部状态,它只在该组件内有效。

共享状态管理

对于跨组件的状态共享,Svelte 提供了 store 概念。我们可以使用 svelte/store 模块来创建可共享的状态。

首先,在项目的 src 目录下创建一个 stores.js 文件,内容如下:

import { writable } from'svelte/store';

export const sharedCount = writable(0);

在上述代码中,使用 writable 创建了一个名为 sharedCount 的可写存储,初始值为 0。

然后在 MyComponent.svelteApp.svelte 中使用这个共享状态。

MyComponent.svelte 中:

<script>
    import { sharedCount } from './stores.js';
    import { onMount } from'svelte';

    let localCount;

    onMount(() => {
        sharedCount.subscribe((value) => {
            localCount = value;
        });
    });

    const incrementSharedCount = () => {
        sharedCount.update((n) => n + 1);
    };
</script>

<div>
    <h2>Shared Count: {localCount}</h2>
    <button on:click={incrementSharedCount}>Increment Shared Count</button>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

App.svelte 中:

<script>
    import MyComponent from './MyComponent.svelte';
    import { sharedCount } from './stores.js';
    import { onMount } from'svelte';

    let appCount;

    onMount(() => {
        sharedCount.subscribe((value) => {
            appCount = value;
        });
    });
</script>

<main>
    <h1>My Svelte App</h1>
    <p>App - level Shared Count: {appCount}</p>
    <MyComponent />
</main>

<style>
    main {
        text - align: center;
        padding: 1em;
        max - width: 240px;
        margin: 0 auto;
    }
</style>

在上述代码中:

  • MyComponent.svelte:通过 sharedCount.subscribe 订阅 sharedCount 的变化,并在按钮点击时通过 sharedCount.update 更新共享状态。
  • App.svelte:同样订阅 sharedCount 的变化,并显示在页面上。这样,MyComponentApp.svelte 之间实现了状态共享。

组件的样式作用域

样式作用域原理

Svelte 组件的样式默认是作用域化的,即组件内定义的样式只应用于该组件,不会影响其他组件。这是通过在编译时为组件的 HTML 元素添加唯一的属性,并在 CSS 选择器中使用该属性来实现的。

示例

MyComponent.svelte 中,我们定义的样式:

<script>
    export let title = 'Default Title';
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

这个样式只会应用于 MyComponent 组件内的 div 元素,不会影响其他组件中的 div 元素。

全局样式

如果需要定义全局样式,可以使用 :global 选择器。

MyComponent.svelte 中添加全局样式:

<script>
    export let title = 'Default Title';
</script>

<div>
    <h2>{title}</h2>
    <p>{message}</p>
</div>

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }

   :global(body) {
        font - family: Arial, sans - serif;
    }
</style>

在上述代码中,:global(body) 定义的样式会应用到整个页面的 body 元素上。

组件的过渡与动画

过渡效果

Svelte 提供了简单而强大的过渡效果支持。可以使用 transition 指令来为组件的插入和移除添加过渡效果。

MyComponent.svelte 中添加过渡效果:

<script>
    import { fade } from'svelte/transition';
    let isVisible = true;

    const toggleVisibility = () => {
        isVisible =!isVisible;
    };
</script>

<button on:click={toggleVisibility}>Toggle Visibility</button>

{#if isVisible}
    <div transition:fade>
        <h2>{title}</h2>
        <p>{message}</p>
    </div>
{/if}

<style>
    div {
        background - color: lightblue;
        padding: 20px;
        border - radius: 5px;
    }
</style>

在上述代码中:

  • <script> 部分:导入了 fade 过渡效果,定义了 isVisible 变量来控制组件的显示与隐藏,以及 toggleVisibility 函数来切换 isVisible 的值。
  • <div> 部分:通过 transition:fadediv 元素添加了淡入淡出的过渡效果。当 isVisibletrue 时,div 元素会淡入显示;当 isVisiblefalse 时,div 元素会淡出隐藏。

动画效果

Svelte 也支持动画效果,可以使用 animate 指令来创建动画。

MyComponent.svelte 中添加动画效果:

<script>
    import { animate } from'svelte/animate';
    let value = 0;

    const incrementValue = () => {
        value++;
    };
</script>

<button on:click={incrementValue}>Increment Value</button>

<div style="width: {value * 50}px; height: 50px; background - color: lightblue" animate:width="{{ duration: 500, easing: 'ease - out' }}">
    <p>{value}</p>
</div>

<style>
    div {
        border - radius: 5px;
    }
</style>

在上述代码中:

  • <script> 部分:定义了 value 变量和 incrementValue 函数,每次点击按钮 value 会增加。
  • <div> 部分:通过 animate:widthdiv 元素的宽度变化添加动画效果。duration 设置动画时长为 500 毫秒,easing 设置缓动函数为 ease - out。随着 value 的变化,div 的宽度会平滑地增加。

通过以上内容,你已经对 Svelte 组件化开发有了一个全面的入门了解,从创建第一个组件,到使用属性、处理事件、管理生命周期、使用插槽、状态管理、样式作用域以及过渡与动画等,这些知识将为你进一步深入 Svelte 开发打下坚实的基础。