数字化开发者大会:混合云 (亚太地区专场:9 月 24 日 上午 11:00) 即刻报名

Svelte 及其核心概念简介

在本教程中,我将介绍 Svelte,这是一个用于构建前端用户界面和应用程序的编译器。了解什么是 Svelte,为什么要使用 Svelte,并动手实践使用 Svelte 构建可重用的搜索组件来过滤字符串数组。跟随学习,您将明白 Svelte 的核心概念,包括反应性、双向绑定、模板语法、作用域样式和槽等。

Svelte 是由 Rich Harris 创造的,这是一个用 TypeScript 编写的开源项目,并且发布到 npm,可用于构建 Web 应用程序。Svelte 的最新主要版本是 V3,于 2019 年 4 月 21 日发布。

预估时间

完成本教程大约需要 20 分钟。

前提条件

要遵循本教程,应确保您已安装了 Node.jsnpm(V5.2 或更高版本)。对于 MacOS,可以使用 Homebrew 来安装必备软件。

为何使用 Svelte?

与 React、Angular 或 Vue 等其他前端框架和库相比,Svelte 可以构建(即编译)捆绑包非常小的 Web 应用程序。Svelte 不是使用具有固定运行时成本的虚拟 DOM,而是在 构建步骤 上执行大多数优化操作。因此,交付给最终用户的 JavaScript 更少,从而缩短了下载和启动时间。

此外,Svelte 还使用简洁的语法,帮助您编写更少的样板代码。Svelte 组件遵循单一文件组件 (SFC) 设计,其中业务逻辑 (JavaScript) 与样式 (CSS) 和标记(markup,类似于 HTML 的模板语法)并置。

例如,Svelte 组件的结构由以下几部分组成:

<!-- Component.svelte -->

<script>
  /* JavaScript ("business logic") */
</script>

<style>
  /* CSS (scoped to the component) */
  h1 {
    font-size: 2rem;
  }
</style>

<!-- Markup -->
<h1>Hello world</h1>

搭建一个新项目

对于现代 Web 应用程序开发,强烈建议使用模块捆绑程序,例如 RollupWebpack。在本教程中,我使用 Svelte Rollup 官方模板

在命令行界面 (CLI) 中运行以下命令来搭建新项目:

$ npx degit sveltejs/template svelte-app

您应该在本地计算机上看到一个名为“svelte-app”的新文件夹,其中包含下载的模板。在文件夹内,通过 npm 安装项目依赖项:

$ cd svelte-app
$ npm install

安装了依赖项之后,执行 npm run dev 以在开发模式下启动项目。访问 http://localhost:5000 以查看应用。

$ npm run dev

您对 src 文件夹中的文件所做的任何更改将自动重新加载在 http://localhost:5000 上运行的应用。这将使开发过程更加高效。

构建搜索组件

src 文件夹中创建一个名为 Search.svelte 的新组件。

$ touch src/Search.svelte

在文件中包含以下内容。

<!-- Search.svelte -->
<script>
  let value = "";
</script>

<input type="search" bind:value />

在输入元素中键入内容时,脚本块中声明的 value 变量将会相应更新。

bind: 指令说明了双向绑定的概念。在这种情况下,当用户在输入元素中键入内容时,Svelte 会自动更新 value

但是,保存文件后,由于组件尚未在应用程序中实例化,因此不会重新加载应用程序。

App.svelte 中现有的内容替换为以下内容:

<!-- App.svelte -->
<script>
  import Search from "./Search.svelte";
</script>

<Search />

导入并实例化 Search 组件后,保存 App.svelte 文件。现在,在重新加载应用程序时,您应该在浏览器中看到输入元素。

接下来,我们将为可以在 App.svelte 中控制的 Search 组件定义属性。

定义并导出名为 autofocusdata 的变量:

<!-- Search.svelte -->
<script>
  export let autofocus = false;
  export let data = [];

  let value = "";
</script>

<input type="search" bind:value />

对于要由使用者定义的导出变量,必须使用 let 语句对其进行初始化。如果未指定任何值,则可以为每个导出的变量分配默认值。

默认情况下,autofocus 初始化为 false,而 data 则是空数组。

App.svelte 中,您可以将值作为属性传递给实例化的 Search 组件。

<!-- App.svelte -->
<script>
  import Search from "./Search.svelte";

  const data = [
    "Bare Metal Server",
    "Blockchain Platform",
    "Db2 Warehouse",
    "Db2",
    "Metrics Server",
    "Node.js",
    "Virtual Machine",
    "Virtual Private Server",
  ];
</script>

<Search autofocus="{true}" data="{data}" />

使用 Svelte 的属性速记语法,您可以将 <Search> 代码修改为:

<Search autofocus {data} />

Search 组件中,如果 autofocustrue,则以编程方式聚焦输入元素。

定义一个名为 input 的变量,然后使用 bind:this 指令将其传递给 input 元素。只有在将输入元素呈现给文档对象模型 (DOM) 之后,才能访问对输入元素的引用。

<!-- Search.svelte -->
<script>
  export let autofocus = false;
  export let data = [];

  let input = undefined;
  let value = "";
</script>

<input bind:this="{input}" type="search" bind:value />

然后,从 Svelte 导入 onMount 生命周期方法。在 onMount 函数内,如果启用了 autofocus,则可以对输入元素调用 .focus() 方法

<!-- Search.svelte -->
<script>
  export let autofocus = false;
  export let data = [];

  import { onMount } from "svelte";

  let input = undefined;
  let value = "";

  onMount(() => {
    if (autofocus) {
      input.focus();
    }
  });
</script>

<input bind:this="{input}" type="search" bind:value />

保存 Search.svelte 之后,应在重新加载时聚焦输入元素。

现在,让我们在 Search 组件中将 data 呈现为具有一些基本样式的无序列表。默认情况下,如果 value 为空字符串,它将会在 UI 中列出数组中的所有项目。

<!-- Search.svelte -->
<script>
  export let autofocus = false;
  export let data = [];

  import { onMount } from "svelte";

  let input = undefined;
  let value = "";

  onMount(() => {
    if (autofocus) {
      input.focus();
    }
  });
</script>

<style>
  ul {
    list-style: none;
  }
</style>

<input bind:this="{input}" type="search" bind:value />

<ul>
  {#each data as item}
  <li>{item}</li>
  {/each}
</ul>

#each 模板语法用于遍历数组。样式块中定义的 ul 元素的 CSS 规则的作用域是组件。换句话说,在 Search.svelte 中为 ul 定义的规则将不会应用于组件外部的 ul 元素。

在您的应用程序中,您现在应该看到 data 数组在输入元素下以列表形式呈现。

现在,让我们来基于 value 过滤 data 列表。

<!-- Search.svelte -->
<script>
  export let autofocus = false;
  export let data = [];

  import { onMount } from "svelte";

  let input = undefined;
  let value = "";

  onMount(() => {
    if (autofocus) {
      input.focus();
    }
  });

  $: filtered = data.filter((item) =>
    item.toLowerCase().includes(value.toLowerCase())
  );
</script>

<style>
  ul {
    list-style: none;
  }
</style>

<input bind:this="{input}" type="search" bind:value />

<ul>
  {#each data as item}
  <li>{item}</li>
  {/each}
</ul>

使用标签语法(例如,$:)将 filtered 表示为反应式分配。

$: filtered = data.filter((item) =>
  item.toLowerCase().includes(value.toLowerCase())
);

无论对 data 作何更改,都会重新计算 filtered 的值。对 data 使用 .filter() 数组方法,以避免改变 data 值。

对于这个基本示例,我们将执行直接字符串比较来过滤列表。

#each 块中,将 data 替换为 filtered

<!-- Search.svelte -->
<script>
  export let autofocus = false;
  export let data = [];

  import { onMount } from "svelte";

  let input = undefined;
  let value = "";

  onMount(() => {
    if (autofocus) {
      input.focus();
    }
  });

  $: filtered = data.filter((item) =>
    item.toLowerCase().includes(value.toLowerCase())
  );
</script>

<style>
  ul {
    list-style: none;
  }
</style>

<input bind:this="{input}" type="search" bind:value />

<ul>
  {#each filtered as item}
  <li>{item}</li>
  {/each}
</ul>

预览您保存的更改。现在,当您在搜索输入中键入内容时,Search 组件应该会过滤该列表。

如何提高该组件的可重用性?

一种方法是延迟向使用者呈现该列表。

要实现延迟呈现,可以将 ul 元素包装在 slot 元素中,并以 `filtered’ 作为属性。

<!-- Search.svelte -->
<script>
  export let autofocus = false;
  export let data = [];

  import { onMount } from "svelte";

  let input = undefined;
  let value = "";

  onMount(() => {
    if (autofocus) {
      input.focus();
    }
  });

  $: filtered = data.filter((item) =>
    item.toLowerCase().includes(value.toLowerCase())
  );
</script>

<style>
  ul {
    list-style: none;
  }
</style>

<input bind:this="{input}" type="search" bind:value />

<slot {filtered}>
  <ul>
    {#each filtered as item}
    <li>{item}</li>
    {/each}
  </ul>
</slot>

调用 Search 组件时,默认情况下会呈现默认列表。但是,使用者可以通过定义以下子元素来呈现自己的标记:

<!-- App.svelte -->
<script>
  import Search from "./Search.svelte";

  const data = [
    "Bare Metal Server",
    "Blockchain Platform",
    "Db2 Warehouse",
    "Db2",
    "Metrics Server",
    "Node.js",
    "Virtual Machine",
    "Virtual Private Server",
  ];
</script>

<Search autofocus {data} let:filtered>
  <ul>
    {#each filtered as item}
    <li>{item}</li>
    {/each}
  </ul>
</Search>

通过 let: 指令访问 filtered 槽属性。Search 中的槽元素会被替换为 App.svelte 中的子元素。

总结

本教程演示了 Svelte 的基本概念(反应性),定义了组件属性、指令和槽,这些都是 Svelte 的基础知识。

可以通过运行以下命令来构建应用程序:

$ npm run build

Rollup 模块捆绑程序可编译 Svelte 应用程序以用于生产。

有关后续步骤,可深入研究官方的 Svelte API 文档,了解有关组件指令、转发的事件、生命周期方法、状态管理等方面的更多信息。

观看视频

本视频中,我将带您大致浏览本教程中涵盖的内容,向您介绍 Svelte 的概念并展示如何使用。

本文翻译自:Introduction to Svelte and its core concepts(2020-05-27)