发布于 2026-01-06 0 阅读
0

如何在周末学会Svelte

如何在周末学会Svelte

我使用 Vue 和 Angular 已经很多年了,最近尝试了 React。让我惊讶的是,React 的学习难度比之前的框架要低得多。最直接的答案当然是 React 更容易上手,但我并没有觉得需要学习的东西更少。我想,或许是我学习的方式起了很大的作用。为了验证我的想法,我需要另一个框架。我选择了 Svelte,它是最新、最小的框架。我整理了思路,准备用同一个应用程序来构建它。令我惊喜的是,我只用了两天时间就完成了 Svelte 的学习和应用程序的构建。

与其从头到尾阅读文档或观看冗长的视频课程,我决定通过构建一个基础但功能强大的应用程序——著名的 TodoMVC 应用——来学习 Svelte 。有些人可能会说,TodoMVC 对于学习框架来说太简单了:如果只使用最少的工具集,这或许没错;但如果将其拆分成多个组件,并进行状态管理和测试,那就未必合适了。

学习框架可能很耗时。对我来说,大幅提升学习效率的方法是将学习过程分解成易于理解的小章节。从基础模块入手,逐步过渡到更复杂的模块,并阅读官方文档中必要的章节,这是正确的学习方法。这样一来,学习过程就不会让人感到不知所措,而且我总能取得一些小小的进步。

我会按照我学习的顺序,一步步地讲解各个构建模块。你可以根据需要添加其他模块,但我认为我提到的这些是理解框架工作原理所必需的。你可以在我的 GitHub 仓库中查看最终的应用程序。如果你想使用其他主流框架来加快开发速度,可以查看我的其他仓库,并按照下面提到的构建模块进行学习(VueVue Composition APIAngularReactReact Hooks)。

成分

组件是 Svelte 应用的构建基石。我们将从组件入手,稍后再添加状态管理。如果您使用官方模板,它会为您提供一个开箱即用的简单组件。

创建

让我们从头开始创建一个静态组件。文档

<script>console.log('runs once');</script>
<style>.main { display: inline; }</style>
<div class="main">Hello World</div>
Enter fullscreen mode Exit fullscreen mode

组件位于 .svelte 文件中,使用 HTML 的超集。该文件包含三个部分:

  • script标签包含 JavaScript 代码,该代码会在组件实例创建时运行一次。
  • style标签包含作用于组件的 CSS 代码
  • 所有其他常规 HTML 标签都被视为组件模板的一部分。

显示和绑定

现在我们需要通过添加状态并将其包含在模板中来使其动态化。文档

<script>
  let firstName = 'Chris';
  const countries = ['Canada'];
  let isVisible = true;
</script>
<style>.main { display: inline; }</style>
<div class:main={isVisible}>
  <h1>{firstName}</h1>
  <input name="first_name" value={firstName} />
  {#if isVisible}
    <ul>
      {#each countries as country }
        <li>{country.toUpperCase()}</li>
      {/each}
    </ul>
  {/if}
</div>
Enter fullscreen mode Exit fullscreen mode

所有在组件模板中声明的块级作用域变量都可以在模板内部使用,并且可以用花括号括起来显示或赋值给属性。条件语句和循环语句使用以 `.` 开头的特殊语法{#。花括号内的表达式会被视为 JavaScript 表达式。

通过交互进行修改

之前的组件是动态的,但无法接收用户的任何输入。添加事件处理程序是使组件具有交互性的一种方法。文档

<script>
  let firstName = 'Chris';
  const modifyName = event => (firstName = event.target.value);
</script>
<div>
  <h1>{firstName}</h1>
  <input name="first_name" value={firstName} on:input={modifyName} />
</div>
Enter fullscreen mode Exit fullscreen mode

默认情况下,局部声明的变量是响应式的。响应式意味着如果我们修改变量的值,它会做出相应的反应。在 `<script>`script标签中声明的函数可用于事件处理。

亲子互动

如果我们不打算把所有功能都放在一个大型组件里,就必须把它拆分成多个组件。这些组件之间将存在父子关系,并通过属性和事件进行通信。文档

<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();

  export let name;
  const modifyName = event => dispatch('modify', event.target.value);
</script>
<div>
  <h1>{name}</h1>
  <input name="first_name" value={name} on:input={modifyName} />
</div>
Enter fullscreen mode Exit fullscreen mode

子组件通过触发一个事件来通知父组件,该事件的返回值来自父组件的函数createEventDispatcher。声明并导出一个变量使得父组件能够将同名属性传递给子组件。此外,该变量还具有响应式特性:如果父组件修改了属性的值,子组件中的该变量的值也会随之改变。

<script>
  import NameInput from './name-input.svelte';

  let firstName = 'Chris';
  const modifyFirstName = event => firstName = event.detail;
</script>
<div>
  <NameInput name={firstName} on:modify={modifyFirstName} />
</div>
Enter fullscreen mode Exit fullscreen mode

父组件首先需要导入子组件,并在其模板中使用驼峰式命名引用它。属性以 props 的形式传递,自定义事件则绑定为原生事件监听器。

生命周期

有时需要在组件生命周期的某些阶段执行某些操作,例如在安装、更新等阶段。

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

  let firstName = 'Chris';
  onMount(() => console.log('mounted'));
</script>
<h1>{firstName}</h1>
Enter fullscreen mode Exit fullscreen mode

on每次生命周期事件发生时,作为参数传递给以 `callback` 开头的方法的回调函数都会被执行。

国家管理

当多个组件使用相同的数据,并且通过多个层传递 props 变得繁琐时,状态管理库可以帮助解决这个问题。

创建店铺

Svelte 内置了 store 实现,无需导入外部库。文档

import { writable, derived } from 'svelte/store';

export const name = writable('Chris');
export const upperCaseName = derived(name, current => current.toUpperCase());
Enter fullscreen mode Exit fullscreen mode

我们可以使用该writable方法创建一个 store。状态的修改值可以通过derivedgetter 获取。每当原始值发生变化时,它都会调用derived函数内部的回调函数。

<script>
  import { name, upperCaseName } from './store';

  const modifyName = event => name.set(event.target.value);
</script>
<div>
  <h1>{$upperCaseName}</h1>
  <input name="first_name" value={$name} on:input={modifyName} />
</div>
Enter fullscreen mode Exit fullscreen mode

存储值显示方式与本地声明的变量在$前缀处有所不同。在底层,Svelte 会subscribe在每次值更改时调用存储操作。

全球访问

在应用程序的各个位置访问 store 至关重要。直接引用 store 是一种快捷的解决方案,但会导致 store 与组件之间紧密耦合。让我们来看看如何在松耦合的情况下访问 store。文档

<script>
  import { setContext } from 'svelte';
  import { name, upperCaseName } from './store';
  import NameInput from './name-input.svelte';

  setContext('name', name);
  setContext('upperCaseName', upperCaseName);
</script>
<div>
  <NameInput />
</div>
Enter fullscreen mode Exit fullscreen mode

setContext方法创建一个上下文变量,该变量只能在组件及其子组件内部访问。

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

  const name = getContext('name');
  const upperCaseName = getContext('upperCaseName');

  const modifyName = event => name.set(event.target.value);
</script>
<div>
  <h1>{$upperCaseName}</h1>
  <input name="first_name" value={$name} on:input={modifyName} />
</div>
Enter fullscreen mode Exit fullscreen mode

可以通过该方法访问上下文变量(在本例中为 store)getContext

测试

如果不进行测试,还有什么能保证应用程序按预期运行呢?Svelte 没有推荐的测试方法,我们必须自行选择测试工具。

单元

单元测试是针对一个独立单元(组件、类等)编写的测试。我们可以使用Jest 测试框架来运行这些测试。文档

<script>
  let firstName = 'Chris';
  const modifyName = event => (firstName = event.target.value);
</script>
<div>
  <h1 data-testid="display">{firstName}</h1>
  <input data-testid="input" value={firstName} on:input={modifyName} />
</div>
Enter fullscreen mode Exit fullscreen mode

我们将重用之前引入的组件。

import { render } from '@testing-library/svelte';
import NameInput from './name-input.svelte';

describe('CopyRight', () => {
  it('should render component', () => {
    const { getByTestId } = render(NameInput);

    const inputElement = getByTestId('input');
    inputElement.value = 'Alexa';
    fireEvent.input(inputElement);

    expect(getByTestId('display')).toHaveTextContent('Alexa');
  });
});
Enter fullscreen mode Exit fullscreen mode

测试用例由describe代码块组织,并由it代码块运行。Svelte Testing 库旨在简化与组件测试相关的日常任务。

端到端

端到端测试将应用程序视为黑盒进行测试,通常在浏览器中运行。我们可以使用Cypress 测试框架来完成这项任务。文档

describe('Display Name', () => {
  it('it should change name', () => {
    cy.visit('/');

    cy.contains('h1', 'Chris');

    cy.get('input').type('Alexa');

    cy.contains('h1', 'Alexa');
  });
});
Enter fullscreen mode Exit fullscreen mode

测试用例的组织方式与代码块相同describeit区别在于我们使用全局cy对象来发出命令。

其他区域

我们讨论的主题足以满足入门级应用的需求,但更复杂的应用还需要用到路由(文档)、请求处理(文档)和动画等其他方面。我的目标是全面了解框架,因此我特意省略了这些内容,因为它们并非必要。

概括

学习框架可能充满挑战,但如果我们明确理解框架所需的关键要素,就能显著缩短学习时间。将需要学习的内容列出清单,能让学习过程更易于理解和衡量。更重要的是,我们总能在学习过程中取得一些小小的成功。我希望这能促使你尝试一下你一直在考虑的新框架。

文章来源:https://dev.to/sonicoder/how-to-learn-svelte-in-a-weekend-4fel