更轻更快 - Svelte 框架指南

已发表: 2022-03-11

Web 应用程序日益流行。 它们是一个不断发展的世界,人们选择它是因为它的简单性、速度和跨平台可用性。 单页应用程序 (SPA) 在此过程中发挥了重要作用。 Angular、Vue.js 和 React 等框架可帮助开发人员在短时间内提供最佳用户体验,从而使代码具有可支持性和可扩展性。 这些工具长期以来一直在该领域最受欢迎,与新创建的软件包相比具有许多优势。 在 SPA 世界中,它感觉就像是寡头垄断。 然而,一群瞄准这个市场的前瞻性开发商可能会与一个严肃的竞争对手——Svelte 一起进入。

Svelte 是一种构建用户界面的新方法。 让我们通过创建一个通用的登录表单来深入探索是什么让它如此新鲜。

建筑学

Svelte 的架构比任何其他库都快。 它是通过改变加载框架以构建虚拟 DOM 的步骤来实现的。 它不是在运行过程中使用工具,而是在构建阶段编译为 vanilla JS,因此应用程序不需要依赖项即可启动。

苗条其他 SPA 库(React、Vue.js、Angular 等)

1.打开一个网站
2.使用纯JS渲染页面

1.打开一个网站
2.等待构建虚拟DOM的代码加载完成
3.使用库渲染页面

上表描述了为什么 Svelte 在启动性能方面绝对是赢家。 这不是通过任何优化获得的,而是通过使用可用的浏览器 JavaScript 编译器而不是辅助编译器获得的。

安装

Svelte 安装非常简单,使用起来非常愉快。 第一步是下载项目的模板:

 npx degit sveltejs/template svelte-login-form

完成上述命令意味着我们有了一个 Svelte 项目模板。 目前它是空的,还没有安装所需的 NPM 包。 让我们解决这个问题。

 cd svelte-login-form npm install

现在应用程序已准备好使用以下命令启动:

 npm run dev

结构

任何 Svelte 组件都可能包含以下部分:

  • 脚本
  • 风格
  • 模板

让我们看一下src/App.svelte文件中的示例。

 <script> export let name; </script> <style> h1 { color: purple; } </style> <h1>{name}</h1>

上面的代码正好包含三个部分:

  1. script标签,这是一个可选的 JavaScript 块,其中包含应在组件内使用的变量和函数声明。

  2. style标签,这是另一个可选块。 除了一个重要的区别之外,它很像一个常见的 HTML 样式标签。 此块中描述的规则仅适用于此组件。 将样式应用于p元素不会影响页面上的所有段落。 这太棒了,因为您不必想出类名,而且您永远不会意外地覆盖另一个规则。

  3. 最后一个也是唯一需要的块是一个模板块——在本例中是一个h1标签。 这是您的组件的演示/视图。 它与样式和脚本块紧密绑定,因为它们决定了视图的样式和行为方式。

Svelte 是一个试图将模块化引入前端游戏的库。 它不仅在分离不同的组件方面而且在隔离逻辑、视图和模板方面保持这种模块化。

回到我们正在构建的登录表单,让我们在src文件夹中创建一个新文件LoginForm.svelte ,其内容如下:

 <style> form { background: #fff; padding: 50px; width: 250px; height: 400px; display: flex; flex-direction: column; justify-content: center; align-items: center; box-shadow: 0px 20px 14px 8px rgba(0, 0, 0, 0.58); } label { margin: 10px 0; align-self: flex-start; font-weight: 500; } input { border: none; border-bottom: 1px solid #ccc; margin-bottom: 20px; transition: all 300ms ease-in-out; width: 100%; } input:focus { outline: 0; border-bottom: 1px solid #666; } button { margin-top: 20px; background: black; color: white; padding: 10px 0; width: 200px; border-radius: 25px; text-transform: uppercase; font-weight: bold; cursor: pointer; transition: all 300ms ease-in-out; } button:hover { transform: translateY(-2.5px); box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.58); } h1 { margin: 10px 20px 30px 20px; font-size: 40px; } </style> <form> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" /> <label>Password</label> <input name="password" type="password" placeholder="password" /> <button type="submit">Log in </button> </form>

这是一个愚蠢的样式组件,我们稍后会变得更智能。 要在我们的网站上看到这个组件,我们应该在根组件 - App 中呈现它。 让我们去编辑src/App.svelte ,让它看起来像这样:

 <script> import LoginForm from "./LoginForm.svelte"; </script> <style> section { height: 100vh; width: 100%; display: flex; justify-content: center; align-items: center; background: linear-gradient(to right, #cd76e2, #e358ab); } </style> <section> <LoginForm /> </section>

如果一切都正确完成并且应用程序仍在运行,我们的表单将出现在localhost:5000 。 让我们通过让表单更智能来提升我们的 Svelte 技能。

进入有状态

Svelte 中的任何组件都可以有自己的状态。 状态是可以在模板内部使用的一个特殊变量或一组特殊变量。 为什么我说“特别”? 每当更改此类变量时,都会通知模板并以最新状态呈现内容。 这允许应用程序非常快速地对用户交互做出反应。

我们将声明emailpassword状态变量,其中将存储适当字段的表单值。 这意味着我们的emailpassword变量将始终与表单值同步,因此我们随时准备提交这些值,而不必担心提交值与表单中的实际值之间存在任何差异。

 <script> let email = ""; let password = ""; let isLoading = false; const handleSubmit = () => { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; // Authorize the user }, 1000); }; </script> <style> /* Style is unchanged */ </style> <form on:submit|preventDefault={handleSubmit}> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> {#if isLoading}Logging in...{:else}Log in {/if} </form>

状态变量看起来像普通的 JavaScript 变量,但是为了使它们与表单值同步(将它们绑定到表单字段),有必要使用bind:value指令。 还有一些不熟悉的地方:

  • on:submit|preventDefault是防止默认事件行为的简写。 这种方式比每次都必须编写e.preventDefault()更舒服。

  • {#if isLoading}Logging in...{:else}Log in {/if}是 Svelte 模板语法的一部分。 由于模板块中没有 JS,因此使用 ifs、循环等有一种特殊的语法。

最后,让我们使用可用的选项通过使用状态来向我们的表单添加验证。 可以通过创建另一个状态变量errors来实现,当表单提交无效值时,该变量将被错误填充。

 <script> let email = ""; let password = ""; let isLoading = false; let errors = {}; const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; // Authorize the user }, 1000); } }; </script> <style> // Previous styles unchanged .errors { list-style-type: none; padding: 10px; margin: 0; border: 2px solid #be6283; color: #be6283; background: rgba(190, 98, 131, 0.3); } </style> <form on:submit|preventDefault={handleSubmit}> <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> <button type="submit"> {#if isLoading}Logging in...{:else}Log in {/if} </button> {#if Object.keys(errors).length > 0} <ul class="errors"> {#each Object.keys(errors) as field} <li>{field}: {errors[field]}</li> {/each} </ul> {/if} </form> 
登录表单错误

表格几乎完成了。 唯一剩下的是成功验证后的成功消息。

让我们创建一个用于跟踪成功提交的状态变量,默认情况下为false 。 成功提交表单后,此变量的值应设置为true

 let isSuccess = false;

处理表单提交的函数也应该更改为在操作成功后遵循切换isSuccess的逻辑。

 const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; // Simulate network request setTimeout(() => { isLoading = false; isSuccess = true; // Authorize the user }, 1000); } };

此修改使表单在提交完成后立即进入成功状态。

但是如果你检查你的开发服务器,你不会发现表单的行为有任何变化。 我们更改了代码,但还没有触及模板。 我们需要在模板中添加说明,以便在用户成功登录时显示成功消息。Svelte 的模板语法允许我们轻松实现:

 <form on:submit|preventDefault={handleSubmit}> {#if isSuccess} <div class="success"> <br /> You've been successfully logged in. </div> {:else} <h1></h1> <label>Email</label> <input name="email" placeholder="[email protected]" bind:value={email} /> <label>Password</label> <input name="password" type="password" bind:value={password} /> <button type="submit"> {#if isLoading}Logging in...{:else}Log in {/if} </button> {#if Object.keys(errors).length > 0} <ul class="errors"> {#each Object.keys(errors) as field} <li>{field}: {errors[field]}</li> {/each} </ul> {/if} {/if} </form>

带有属性的抽象

我们已经整理了有关内部组件状态的所有内容。 现在是时候了解称为属性或“props”的外部依赖项了。 Props 是传递给组件的输入或参数,用于向组件描述应该出现的内容或组件的行为方式。

除了关键字export之外,属性的声明看起来与状态相似。

 <script> export let answer; </script> <p>The answer is {answer}</p>
 <script> import Nested from './Nested.svelte'; </script> <Nested answer={42}/>

都是关于属性的。 声明并通过 - 所有你需要知道的使用道具。

但是这些属性如何应用于登录表单组件呢? Props 可以通过将提交函数提取到一个属性中来使我们的登录表单更加通用。 它将允许您将此组件与您需要的任何提交操作一起使用(请求测试服务器,请求实际服务器等)。 这个 prop 将被称为submit并且将是一个函数,如果提交操作成功则返回已解决的承诺,如果有错误则返回被拒绝的承诺。 让我们通过上面给出的示例声明 prop:

 export let submit;

登录表单中的提交处理程序也应该被编辑以使用新的submit属性。

 const handleSubmit = () => { errors = {}; if (email.length === 0) { errors.email = "Field should not be empty"; } if (password.length === 0) { errors.password = "Field should not be empty"; } if (Object.keys(errors).length === 0) { isLoading = true; submit({ email, password }) .then(() => { isSuccess = true; isLoading = false; }) .catch(err => { errors.server = err; isLoading = false; }); } };

该组件似乎已准备就绪。 但是,如果您返回表单并尝试提交它,您会注意到按钮的状态从加载开始并没有改变。 此外,控制台中有一个异常: Uncaught TypeError: submit is not a function 。 当然,我们声明了 prop 但忘了传递它。 让我们在 app 组件中声明一个函数并将其传递给登录表单。

 const submit = ({ email, password }) => new Promise((resolve, reject) => setTimeout(resolve, 1000));
 <section> <LoginForm submit={submit} /> </section>

现在表单正在按预期工作。 它既可以显示错误,也可以通知用户登录是否成功。

登录表单成功

上下文共享

似乎列出了构建应用程序所需的一切。 有了属性和内部状态,我们就可以开始了。 不过,这只是部分正确。 这两点使设计高复杂度的 SPA 成为可能。 但是,如果您尝试在许多不同的组件之间共享数据,您会发现这非常困难。

最简单的例子是拥有一个全局可访问的user变量。 许多组件应该根据用户的角色、年龄、状态等来改变与用户相关的行为。但是,通过使用 props 将用户传递给应用程序中的每个组件来重复我们自己并不是 DRY。

Svelte 对此有一个解决方案:上下文 API。

上下文 API 为组件提供了一种机制,可以相互“对话”,而无需将数据和函数作为道具传递或分派大量事件。 这是一项高级功能,但很有用。

让我们将用户上下文添加到我们正在设计的登录表单中。 在src文件夹中创建一个文件userContext.js ,内容如下:

 export const key = "userContext"; export const initialValue = null;

key是上下文的唯一标识符,因为应用程序可能有无限数量的不同上下文必须保持可访问性。 initialValue只是设置之前上下文的默认值。

下一步是将上下文添加到我们的应用程序中。 导航到App.svelte文件并添加 2 个导入语句:

 import { onMount, setContext } from "svelte"; import { key as userContextKey, initialValue as userContextInitialValue } from "./userContext";

查看上面的代码,您可能想知道我们从svelte包中导入了什么。 onMount是一个辅助函数,需要回调函数作为参数。 此回调将在当前组件正在挂载时执行(在加载组件的最开始时)。 setContext是上下文的设置器函数。 它需要上下文的键和一个新值作为其参数。

让我们使用onMount函数来设置上下文的默认值:

 onMount(() => { setContext(userContextKey, userContextInitialValue); });

并修改submit函数以设置用户上下文:

 const submit = ({ email, password }) => new Promise((resolve, reject) => { setTimeout(() => { setContext(userContextKey, { name: "Foo", lastName: "Bar", email: "[email protected]" }); resolve(); }, 1000); });

而已。 成功的提交会将用户上下文更改为可以由上下文获取器getContext访问的假用户对象:

 <script> import { getContext } from 'svelte'; import { key as userContextKey } from "./userContext"; const user = getContext(key); </script>

概括

Svelte 是一个强大的工具,具有高性能和灵活的 API。 除了本文介绍的基础知识外,Svelte 还具有以下开箱即用的功能:

  • 反应式声明和陈述
  • 等待模板块
  • 维度绑定
  • 像 Redux 这样的全球商店
  • 动画和过渡助手
  • 调试助手

总而言之,Svelte 是一个很棒的库,可以满足构建 SPA 的所有需求等等。 它可以与市场上最大的参与者竞争,甚至获胜。 不过,它现在可以使用的是前端开发人员社区的支持。

注意:本文中的所有代码都可以在teimurjan/svelte-login-form GitHub 存储库中找到。 登录表单的演示可在此处获得。