我用 svelte 做了一个小程序

1,871 阅读10分钟

搜索框传播样式-标准色版.png

在过去的两年中,Svelte得到了很多的赞扬,它远远不是“另一个前端框架”。它在2019年JS州调查中获得“年度突破”,随后在2020年满意度排名第一。它还在Stack Overflow 2021年的调查中被选为最受欢迎的web框架。

Svelte吸引开发人员的是其捆绑包小、性能好和易于使用的组合。同时,它也有很多好吃的。已经提供了一个简单的状态管理解决方案,以及随时可用的转换和动画。本入门教程将阐明如何苗条实现这一点。本系列的后续教程将更详细地介绍如何使用Svelte提供的各种可能性来实现应用程序。

但首先,给你讲个关于Sevlte的背景故事。虽然Svelte在本世纪20年代初才进入主流,但它存在的时间要长得多。

GitHub的第一次承诺是在2016年末。它的创造者是Rich Harris,一个开源奇才,他最著名的另一项发明是Rollup。里奇·哈里斯(Rich Harris)当时在新闻杂志《卫报》(the Guardian)担任图形编辑。他的日常工作是为网站创建交互式可视化,他希望有一个工具,可以让他轻松地编写这些内容,而不影响捆绑包的大小或速度。与此同时,他想要一个平易近人的东西,这样其他不太懂技术的同事就可以快速创建可视化。

**正是出于这些需求,Svelte诞生了。从新闻编辑室开始,Svelte很快在开源社区聚集了一小群追随者。**但直到2019年4月,Svelte才真正为世界所知。这个日期标志着版本3的发布,这是一个完全重写,重点关注开发人员的体验和可接近性。从那以后,Svelte的受欢迎程度上升了很多,越来越多的维护者加入了团队,Rich Harris甚至加入Vercel全职致力于Svelte。

创建一个书籍列表

们将建立一个小的书单,允许我们添加和删除我们的阅读清单上的书。最终结果如下图所示。

我们将从一个项目模板开始搭建我们的项目。我们将使用官方的Svelte模板。另一种选择是使用vite驱动的模板,或者使用SvelteKit,一个基于Svelte的框架,用于构建具有内置路由功能的成熟应用程序——但在本中,我们将尽可能保持它的简洁。

下载完模板后,切换到它的文件夹并运行npm install,它会下载我们需要的所有包。然后我们将切换到App.svelte,在那里我们将替换内容的html版本,以布局我们想要的视觉效果:

<h4>Add Book</h4>
<input type="text" />
<h4>My Books</h4>
<ul>
  <li>A book</li>
</ul>

我们可以直接在Svelte文件的顶层编写上述代码;我们不需要添加任何包装元素。Svelte的语法是HTML的超集,所以任何在HTML文件中有效的内容在Svelte文件中也是有效的。

现在的问题是如何把动态的部分放进去。我们将从向脚本添加一个静态列表开始,并通过循环渲染它:

<script>
  let books = ['Learning Svelte', 'The Zen of Cooking Tea'];
</script>

<label>
  <h4>Add Book</h4>
  <input type="text" />
</label>
<h4>My Books</h4>
<ul>
  {#each books as book}
    <li>{book}</li>
  {/each}
</ul>

我们添加了一个脚本标记,将与组件相关的JavaScript逻辑放入其中。该逻辑在组件每次挂载时执行。我们还用特殊的Svelte语法增强了HTML,以创建一个循环并打印每本书的标题。正如你所看到的,Svelte对于控制流块有不同的语法,不像Vue或Angular,它们以特殊属性的形式添加了这样的功能。这使得代码更具可读性,因为您可以更容易地发现它。如果您希望在控制流块中包含多个顶级项目,那么也不必创建包装器元素。

一本书的标题是用花括号包围变量输出的。通常,当您在模板中遇到花括号时,您就知道您输入的是与svelte相关的内容。我们将在本文的第2部分中更详细地研究模板语法。

响应用户输入

现在,我们可以呈现由books变量定义的任意图书标题列表。增加一本新书怎么样?为此,我们需要增强<script>标签中的逻辑,并将其连接到<input>元素:

<script>
  let books = ['Learning Svelte', 'The Zen of Cooking Tea'];
  let newBook = '';

  function addBook(evt) {
    if (evt.key === 'Enter') {
      books = [...books, newBook];
      newBook = '';
    }
  }
</script>

<label>
  <h4>Add Book</h4>
  <input type="text" bind:value={newBook} on:keydown={addBook} />
</label>
<h4>My Books</h4>
<ul>
  {#each books as book}
    <li>{book}</li>
  {/each}
</ul>

我们添加了一个名为newBook的新变量,它应该反映输入值。为此,我们通过写入bind:value={newBook}将其绑定到<input>。这建立了一个双向绑定,因此每次用户向<input>输入文本时,newBook都会更新,如果newBook在<script>标记中更新,<input>的显示值就会改变。我们本可以对简单的动态属性做同样的工作,但这种方式为我们节省了一些代码——在Svelte中经常遇到的思考模式。

当用户按下enter键时,我们希望将新书标题添加到列表中。为此,我们添加了一个DOM事件监听器。要告诉Svelte钩子到事件,我们只需在on和其余的事件名称之间添加一个冒号——在本例中是on:keydown。在那之后,我们使用花括号并将函数名放在里面。该函数在每次事件触发时被调用。关于这个模板语法的更多信息可以在本系列教程的第2部分中找到。

在本例中要调用的函数是addBook,在这个函数中,我们检查键盘事件,如果用户确实按下了enter,我们就更新books变量。注意,我们在Angular或Vue 2中发现的这种上下文缺少,或者在Vue 3中缺少特殊值对象,或者在React中缺少setState。在这种情况下,Svelte不需要额外的语法来知道变量已经更新。这可能感觉像魔法,但同时也像“简单的JavaScript”。

要了解sevlet是如何做到这一点的,我们需要看看背后的情况。Svelte对.svelte文件实际做了什么,它什么时候处理它?

答案是: Svelte实际上是一个编译器!在代码加载到浏览器之前,它就完成了大部分工作。Svelte解析代码并将其转换成常规的JavaScript。在解析过程中,它能够看到像newBook这样的变量在模板中被使用,所以对它的赋值将导致重新呈现。因此,编译输出将用对$$invalidate函数的调用来包装这些赋值,该函数将为下一次浏览器绘制安排对该组件的重新渲染。这是Svelte出色表现的秘密:它提前知道哪些部分可能触发渲染器,然后只需要在这些确切的位置执行工作,外科手术地更新DOM。这也是为什么Svelte应用程序的包大小如此之小的原因:所有不需要的东西都不会出现在输出中,所以Svelte可以省去运行时中所有不需要的部分。一个sevlet的Hello World!app的 bundle 包大小只有2.5KB!

唯一需要注意的是,sevlet 只会找任务。这就是为什么我们需要做books=[…书,newBook];books.push (newBook);book=books;。否则,sevlet 不会知道 book 更新了。

收尾

我们做到了!我们现在可以查看和添加书籍到我们的列表!不过,它看起来并不那么漂亮,所以让我们对UI进行一些最后的润色。首先,我们将添加一些CSS样式的元素:

<!-- script and html code... -->

<style>
  input {
    padding: 5px 10px;
  }
  li {
    list-style: none;
  }
  ul {
    padding: 5px 0;
  }
</style>

正如你所看到的,我们只是在.svelte文件中添加一个<style>标签,并继续在其中编写常规的CSS。如果您担心上面的代码将样式化整个应用程序中的所有<input>、<li><ul>标记,请放心,它不会。默认情况下,精简作用域样式,所以它们只适用于定义它们的组件。如果你想全局定义一些东西,用:global函数包装选择器。例如,你想要样式化所有的 <input>,可以这样写:

:global(input) { padding: 5px 10px; }.

样式现在好多了。让我们以一个更好的UX过渡来结束它:我们希望新的列表元素淡入。为此,我们只需要找到一个Svelte的内置过渡和动画,并应用它们:

<script>
  import { fade } from 'svelte/transition';
  // ..
</script>

<!-- input ... -->
<h4>My Books</h4>
<ul>
  {#each books as book}
    <li transition:fade>{book}</li>
  {/each}
</ul>

<!-- styling ... -->

只需导入一个内置的过渡,并通过添加过渡transition:fade来应用它,我们就得到了平滑的淡入过渡。我们的迷你应用程序现在完成了。这还不包含顶部栏和背景渐变,但现在你应该很容易添加这一点。这是最终的结果:

<script>
  import { fade } from 'svelte/transition';

  let books = ['Learning Svelte', 'The Zen of Cooking Tea'];
  let newBook = '';

  function addBook(evt) {
    if (evt.key === 'Enter') {
      books = [...books, newBook];
      newBook = '';
    }
  }
</script>

<label>
  <h4>Add Book</h4>
  <input type="text" bind:value={newBook} on:keydown={addBook} />
</label>
<h4>My Books</h4>
<ul>
  {#each books as book}
    <li transition:fade>{book}</li>
  {/each}
</ul>

<style>
  input {
    padding: 5px 10px;
  }
  li {
    list-style: none;
  }
  ul {
    padding: 5px 0;
  }
</style>

我们已经看到了如何用30多行代码编写一个小应用程序。当然,我们只是触及了表面。一个成熟的应用程序需要某种状态管理、多个组件,以及将这些组件相互集成的方法。

例如,将一个待办事项的显示拆分为一个单独的组件是有意义的,因为我们将添加一些功能,如就地编辑名称或将其标记为已完成。将所有这些都放在一个组件中,随着时间的推移将变得难以维护。幸运的是,使用其他组件就像从另一个Svelte文件中导入它作为默认导入一样简单,并以类似于我们已经看到的常规DOM元素的方式与它交互。我们将在本系列的第5部分中更详细地研究组件交互。

另一个例子是待办事项的管理。现在,它们是在组件内部处理的,没有连接到后端。如果我们要添加API调用,我们将把UI逻辑与后端交互混合起来,这通常可以更好地在组件之外处理,以便更好地分离关注点。

准备好使用 svelte 了吗

那么,在你的下一个项目中使用Svelte安全吗?你的经理可能会问,Svelte是否会在未来几年继续存在,或者会像以前的前端框架明星一样被淘汰。目前还没有一家大公司像支持Angular和React那样支持Svelte的整个开发,但Vue已经表明这不是问题。此外,正如一开始所说的,《苗条》的创造者Rich Harris现在正在全职开发这款游戏。随着苗条身材越来越受欢迎,在未来的几年里,它不会有任何发展的迹象。

选择框架的另一个方面是生态系统及其工具。与React相比,这个生态系统仍然很小,但是每天都有新的库出现,而且已经有一些非常好的组件库了。同时,由于Svelte非常接近普通的HTML和JavaScript,所以很容易将任何现有的常规HTML/JavaScript库集成到你的代码库中,而不需要包装器库。

关于工具,Svelte看起来不错。有一个官方的VS Code扩展正在积极维护,以及一个底层的语言服务器,可以被许多其他ide用来集成智能感知。IntelliJ也为Svelte提供了一个插件,并在最近雇佣了它背后的创造者为JetBrains工作。还有各种各样的工具可以将Svelte与各种捆绑器集成在一起。是的,你也可以在Svelte中使用TypeScript。

如果你正在寻找建立一个成熟的网站或web应用程序,你可能也有兴趣检查SvelteKit(见我们的初学者指南的SvelteKit)。它提供了出色的开发体验,并带有灵活的基于文件系统的路由器。它还使您能够部署到许多不同的平台,如Vercel、Netlify、您自己的Node服务器,或者仅仅是一个很好的老式静态文件服务器,这取决于您的应用程序的特性和需求。

简而言之,关于svelte,以下是需要记住的要点:

  • 它有一个全职的维护人员
  • 它有很好的工具
  • 特点稳定
  • 它的生态系统正在成长
  • SvelteKit可用于快速构建应用程序