使用 Svelte 存储
在上一篇文章中,我们完成了应用程序的开发,完成了将其组织成组件的工作,并讨论了一些处理响应性、处理 DOM 节点和公开组件功能的高级技术。在本文中,我们将展示另一种在 Svelte 中处理状态管理的方法:存储。存储是保存值的全局数据存储库。组件可以订阅存储,并在其值发生变化时接收通知。
先决条件 |
至少,建议您熟悉核心 HTML、CSS 和 JavaScript 语言,并了解 终端/命令行。 您需要一个安装了 node 和 npm 的终端来编译和构建您的应用程序。 |
---|---|
目标 | 学习如何使用 Svelte 存储 |
使用存储,我们将创建一个Alert
组件,该组件在屏幕上显示通知,并且可以接收来自任何组件的消息。在这种情况下,Alert
组件独立于其他组件——它不是任何其他组件的父级或子级——因此消息不适合组件层次结构。
我们还将了解如何开发自己的自定义存储以将待办事项信息持久保存到 Web 存储,从而允许我们的待办事项在页面重新加载后仍然存在。
与我们一起编写代码
Git
使用以下命令克隆 GitHub 仓库(如果您尚未执行此操作):
git clone https://github.com/opensas/mdn-svelte-tutorial.git
然后,要进入当前应用程序状态,请运行:
cd mdn-svelte-tutorial/06-stores
或直接下载文件夹的内容
npx degit opensas/mdn-svelte-tutorial/06-stores
请记住运行npm install && npm run dev
以在开发模式下启动您的应用程序。
REPL
要使用 REPL 与我们一起编码,请从以下位置开始:
https://svelte.net.cn/repl/d1fa84a5a4494366b179c87395940039?version=3.23.2
处理应用程序状态
我们已经了解了我们的组件如何使用 props、双向数据绑定和事件相互通信。在所有这些情况下,我们都在处理父组件和子组件之间的通信。
但并非所有应用程序状态都属于应用程序的组件层次结构中。例如,有关登录用户的信息,或是否选择了深色主题。
有时,您的应用程序状态需要由多个与层次结构无关的组件或常规 JavaScript 模块访问。
此外,当您的应用程序变得复杂且组件层次结构变得复杂时,组件之间可能难以相互传递数据。在这种情况下,迁移到全局数据存储可能是一个不错的选择。如果您已经使用过 Redux 或 Vuex,那么您将熟悉这种存储的工作方式。Svelte 存储为状态管理提供了类似的功能。
存储是一个具有subscribe()
方法的对象,该方法允许感兴趣的方在存储值发生变化时收到通知,以及一个可选的set()
方法,该方法允许您为存储设置新值。这个最小的 API 称为 存储契约。
Svelte 提供了用于在svelte/store
模块中创建 可读、可写 和 派生 存储的函数。
Svelte 还提供了一种非常直观的方法,可以使用 反应式$store
语法 将存储集成到其反应式系统中。如果您创建自己的符合存储契约的存储,则可以免费获得此反应式语法糖。
创建 Alert 组件
为了展示如何使用存储,我们将创建一个Alert
组件。这些类型的部件也可能被称为弹出通知、吐司或通知气泡。
我们的Alert
组件将由App
组件显示,但任何组件都可以向其发送通知。每当收到通知时,Alert
组件将负责在屏幕上显示它。
创建存储
让我们从创建一个可写存储开始。任何组件都可以写入此存储,并且Alert
组件将订阅它,并在修改存储时显示消息。
- 在您的
src
目录中创建一个新文件stores.js
。 - 赋予它以下内容js
import { writable } from "svelte/store"; export const alert = writable("Welcome to the to-do list app!");
注意:存储可以在 Svelte 组件外部定义和使用,因此您可以根据需要组织它们。
在上面的代码中,我们从svelte/store
导入writable()
函数,并使用它创建一个名为alert
的新存储,其初始值为“欢迎使用待办事项列表应用程序!”。然后我们export
存储。
创建实际的组件
现在让我们创建我们的Alert
组件,并了解如何从存储中读取值。
- 创建另一个名为
src/components/Alert.svelte
的新文件。 - 赋予它以下内容svelte
<script> import { alert } from '../stores.js' import { onDestroy } from 'svelte' let alertContent = '' const unsubscribe = alert.subscribe((value) => alertContent = value) onDestroy(unsubscribe) </script> {#if alertContent} <div on:click={() => alertContent = ''}> <p>{ alertContent }</p> </div> {/if} <style> div { position: fixed; cursor: pointer; margin-right: 1.5rem; margin-left: 1.5rem; margin-top: 1rem; right: 0; display: flex; align-items: center; border-radius: 0.2rem; background-color: #565656; color: #fff; font-weight: 700; padding: 0.5rem 1.4rem; font-size: 1.5rem; z-index: 100; opacity: 95%; } div p { color: #fff; } div svg { height: 1.6rem; fill: currentcolor; width: 1.4rem; margin-right: 0.5rem; } </style>
让我们详细了解这段代码。
- 在开头,我们导入
alert
存储。 - 接下来,我们导入
onDestroy()
生命周期函数,它允许我们在组件卸载后执行回调。 - 然后我们创建一个名为
alertContent
的局部变量。请记住,我们可以从标记中访问顶级变量,并且每当修改它们时,DOM 都会相应地更新。 - 然后我们调用方法
alert.subscribe()
,将回调函数作为参数传递给它。每当存储的值发生变化时,回调函数将被调用,并将新值作为其参数。在回调函数中,我们只需将接收到的值赋给局部变量,这将触发组件 DOM 的更新。 subscribe()
方法还返回一个清理函数,该函数负责释放订阅。因此,我们在组件初始化时订阅,并在组件卸载时使用onDestroy
取消订阅。- 最后,我们在标记中使用
alertContent
变量,如果用户单击警报,我们将清除它。 - 最后,我们包含几行 CSS 代码来设置我们的
Alert
组件的样式。
此设置允许我们以反应式的方式使用存储。当存储的值发生变化时,将执行回调。在那里,我们将一个新值赋给一个局部变量,并且由于 Svelte 的反应性,我们所有的标记和反应式依赖项都会相应地更新。
使用组件
现在让我们使用我们的组件。
- 在
App.svelte
中,我们将导入组件。在现有导入语句下方添加以下导入语句jsimport Alert from "./components/Alert.svelte";
- 然后在
Todos
调用上方调用Alert
组件,如下所示svelte<Alert /> <Todos {todos} />
- 现在加载您的测试应用程序,您现在应该在屏幕上看到
Alert
消息。您可以单击它将其关闭。
使用响应式 $store
语法使存储变得响应式
这可以工作,但是每次您想要订阅存储时都必须复制粘贴所有这些代码
<script>
import myStore from "./stores.js";
import { onDestroy } from "svelte";
let myStoreContent = "";
const unsubscribe = myStore.subscribe((value) => (myStoreContent = value));
onDestroy(unsubscribe);
</script>
{myStoreContent}
对于 Svelte 来说,这太多了!作为编译器,Svelte 有更多资源来让我们的生活更轻松。在这种情况下,Svelte 提供了反应式$store
语法,也称为自动订阅。简单来说,您只需在存储前添加$
符号,Svelte 将生成代码以使其自动具有反应性。因此,我们之前的代码块可以替换为以下内容
<script>
import myStore from "./stores.js";
</script>
{$myStore}
并且$myStore
将完全具有反应性。这也适用于您自己的自定义存储。如果您实现了subscribe()
和set()
方法,就像我们稍后将要做的,反应式$store
语法也将应用于您的存储。
- 让我们将其应用于我们的
Alert
组件。更新Alert.svelte
的<script>
和标记部分,如下所示svelte<script> import { alert } from '../stores.js' </script> {#if $alert} <div on:click={() => $alert = ''}> <p>{ $alert }</p> </div> {/if}
- 再次检查您的应用程序,您会发现它与之前一样工作。这样好多了!
在幕后,Svelte 生成了代码来声明局部变量$alert
、订阅alert
存储、在存储内容修改时更新$alert
以及在组件卸载时取消订阅。它还将在我们为$alert
分配值时生成alert.set()
语句。
此巧妙技巧的最终结果是,您可以像使用反应式局部变量一样轻松地访问全局存储。
这是一个完美的例子,说明了 Svelte 如何让编译器负责更好的开发者体验,不仅可以节省我们键入样板代码的时间,还可以生成更不易出错的代码。
写入我们的存储
写入我们的存储只是导入它并执行$store = 'new value'
的问题。让我们在我们的Todos
组件中使用它。
- 在现有的导入语句下方添加以下
import
语句jsimport { alert } from "../stores.js";
- 像这样更新您的
addTodo()
函数jsfunction addTodo(name) { todos = [...todos, { id: newTodoId, name, completed: false }]; $alert = `Todo '${name}' has been added`; }
- 像这样更新
removeTodo()
jsfunction removeTodo(todo) { todos = todos.filter((t) => t.id !== todo.id); todosStatus.focus(); // give focus to status heading $alert = `Todo '${todo.name}' has been deleted`; }
- 将
updateTodo()
函数更新为以下内容jsfunction updateTodo(todo) { const i = todos.findIndex((t) => t.id === todo.id); if (todos[i].name !== todo.name) $alert = `todo '${todos[i].name}' has been renamed to '${todo.name}'`; if (todos[i].completed !== todo.completed) $alert = `todo '${todos[i].name}' marked as ${ todo.completed ? "completed" : "active" }`; todos[i] = { ...todos[i], ...todo }; }
- 在以
let filter = 'all'
开头的块下方添加以下反应式块js$: { if (filter === "all") { $alert = "Browsing all to-dos"; } else if (filter === "active") { $alert = "Browsing active to-dos"; } else if (filter === "completed") { $alert = "Browsing completed to-dos"; } }
- 最后,暂时更新
const checkAllTodos
和const removeCompletedTodos
块,如下所示jsconst checkAllTodos = (completed) => { todos = todos.map((t) => ({ ...t, completed })); $alert = `${completed ? "Checked" : "Unchecked"} ${todos.length} to-dos`; }; const removeCompletedTodos = () => { $alert = `Removed ${todos.filter((t) => t.completed).length} to-dos`; todos = todos.filter((t) => !t.completed); };
- 所以基本上,我们导入了存储并在每个事件上更新了它,这会导致每次显示一个新的警报。再次查看您的应用程序,然后尝试添加/删除/更新一些待办事项!
一旦我们执行$alert = …
,Svelte 将运行alert.set()
。我们的Alert
组件——就像 alert 存储的每个订阅者一样——在接收到新值时将收到通知,并且由于 Svelte 的反应性,其标记将被更新。
我们可以在任何组件或.js
文件中执行相同的操作。
注意:在 Svelte 组件外部,您无法使用$store
语法。这是因为 Svelte 编译器不会触及 Svelte 组件外部的任何内容。在这种情况下,您必须依靠store.subscribe()
和store.set()
方法。
改进我们的 Alert 组件
每次都必须单击警报才能将其删除有点烦人。如果通知在几秒钟后自动消失会更好。
让我们看看如何做到这一点。我们将指定一个带有等待毫秒数的 prop,以便在清除通知之前等待,并且我们将定义一个超时以删除警报。我们还将在Alert
组件卸载时清除超时,以防止内存泄漏。
- 像这样更新
Alert.svelte
组件的<script>
部分jsimport { onDestroy } from "svelte"; import { alert } from "../stores.js"; export let ms = 3000; let visible; let timeout; const onMessageChange = (message, ms) => { clearTimeout(timeout); if (!message) { // hide Alert if message is empty visible = false; } else { visible = true; // show alert if (ms > 0) timeout = setTimeout(() => (visible = false), ms); // and hide it after ms milliseconds } }; $: onMessageChange($alert, ms); // whenever the alert store or the ms props changes run onMessageChange onDestroy(() => clearTimeout(timeout)); // make sure we clean-up the timeout
- 并像这样更新
Alert.svelte
的标记部分svelte{#if visible} <div on:click={() => visible = false}> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.432 0c1.34 0 2.01.912 2.01 1.957 0 1.305-1.164 2.512-2.679 2.512-1.269 0-2.009-.75-1.974-1.99C9.789 1.436 10.67 0 12.432 0zM8.309 20c-1.058 0-1.833-.652-1.093-3.524l1.214-5.092c.211-.814.246-1.141 0-1.141-.317 0-1.689.562-2.502 1.117l-.528-.88c2.572-2.186 5.531-3.467 6.801-3.467 1.057 0 1.233 1.273.705 3.23l-1.391 5.352c-.246.945-.141 1.271.106 1.271.317 0 1.357-.392 2.379-1.207l.6.814C12.098 19.02 9.365 20 8.309 20z"/></svg> <p>{ $alert }</p> </div> {/if}
首先,我们创建了一个名为 ms
的 prop,其默认值为 3000(毫秒)。然后,我们创建了一个 onMessageChange()
函数,用于控制 Alert 是否可见。通过 $: onMessageChange($alert, ms)
,我们告诉 Svelte 在 $alert
存储或 ms
prop 发生变化时运行此函数。
每当 $alert
存储发生变化时,我们将清除任何挂起的超时。如果 $alert
为空,我们将 visible
设置为 false
,Alert 将从 DOM 中移除。如果它不为空,我们将 visible
设置为 true
,并使用 setTimeout()
函数在 ms
毫秒后清除 Alert。
最后,使用 onDestroy()
生命周期函数,我们确保调用 clearTimeout()
函数。
我们还在 Alert 段落上方添加了一个 SVG 图标,使其看起来更美观一些。再次尝试一下,您应该可以看到更改。
使我们的 Alert 组件可访问
我们的 Alert
组件运行良好,但对于辅助技术而言并不友好。问题在于动态添加到页面并从中移除的元素。虽然对于可以看到页面的用户来说很明显,但对于使用辅助技术(如屏幕阅读器)的用户来说可能就不那么明显了。为了处理这些情况,我们可以利用 ARIA 实时区域,它提供了一种以编程方式公开动态内容更改的方法,以便辅助技术可以检测和宣布这些更改。
我们可以声明一个包含应由辅助技术宣布的动态内容的区域,方法是使用 aria-live
属性,后跟礼貌设置,该设置用于设置屏幕阅读器处理该区域更新的优先级。可能的设置有 off
、polite
或 assertive
。
对于常见情况,您还有几个预定义的专门 role
值可用,例如 log
、status
和 alert
。
在我们的例子中,只需在 <div>
容器中添加 role="alert"
即可,如下所示
<div role="alert" on:click={() => visible = false}>
通常,使用屏幕阅读器测试您的应用程序是一个好主意,这不仅可以发现可访问性问题,还可以了解视障人士如何使用网络。您有多种选择,例如 Windows 上的 NVDA、Chrome 上的 ChromeVox、Linux 上的 Orca 以及 macOS 和 iOS 上的 VoiceOver,以及其他选项。
要了解有关检测和修复可访问性问题的更多信息,请查看我们的 处理常见可访问性问题 文章。
使用存储契约来持久化我们的待办事项
我们的简易应用程序使我们能够轻松管理待办事项,但如果我们每次重新加载时都获得相同的硬编码待办事项列表,它就没什么用了。为了使其真正有用,我们必须找出如何持久化我们的待办事项。
首先,我们需要某种方法让我们的 Todos
组件将其更新的待办事项返回给其父组件。我们可以使用待办事项列表发出更新事件,但绑定 todos
变量更容易。让我们打开 App.svelte
并尝试一下。
- 首先,在
todos
数组下方添加以下行js$: console.log("todos", todos);
- 接下来,更新您的
Todos
组件调用,如下所示svelte<Todos bind:todos />
注意:
<Todos bind:todos />
只是<Todos bind:todos={todos} />
的简写。 - 返回您的应用程序,尝试添加一些待办事项,然后转到您的开发者工具 Web 控制台。您会看到,由于
bind
指令,我们对待办事项所做的每次修改都会反映在App.svelte
中定义的todos
数组中。
现在我们必须找到一种方法来持久化这些待办事项。我们可以在 App.svelte
组件中实现一些代码,以将我们的待办事项读写到 Web 存储 或 Web 服务中。但是,如果我们可以开发一些通用的存储来持久化其内容,岂不是更好?这将允许我们像使用任何其他存储一样使用它,并抽象化持久化机制。我们可以创建一个将其内容同步到 Web 存储的存储,然后开发另一个与 Web 服务同步的存储。在它们之间切换将非常简单,我们根本不需要触碰 App.svelte
。
保存我们的待办事项
因此,让我们首先使用常规的可写存储来保存我们的待办事项。
- 打开文件
stores.js
,并在现有存储下方添加以下存储jsexport const todos = writable([]);
- 这很容易。现在我们需要导入存储并在
App.svelte
中使用它。请记住,要访问待办事项,现在我们必须使用$todos
反应式$store
语法。像这样更新您的App.svelte
文件svelte<script> import Todos from "./components/Todos.svelte"; import Alert from "./components/Alert.svelte"; import { todos } from "./stores.js"; $todos = [ { id: 1, name: "Create a Svelte starter app", completed: true }, { id: 2, name: "Create your first component", completed: true }, { id: 3, name: "Complete the rest of the tutorial", completed: false } ]; </script> <Alert /> <Todos bind:todos={$todos} />
- 尝试一下;一切应该都能正常工作。接下来,我们将了解如何定义我们自己的自定义存储。
如何实现存储契约:理论
您可以通过实现存储契约来创建自己的存储,而无需依赖 svelte/store
。其功能必须按如下方式工作
- 存储必须包含一个
subscribe()
方法,该方法必须接受订阅函数作为其参数。每当存储的值发生变化时,都必须调用存储的所有活动订阅函数。 subscribe()
方法必须返回一个unsubscribe()
函数,该函数在被调用时必须停止其订阅。- 存储可以选择包含一个
set()
方法,该方法必须接受存储的新值作为其参数,并且同步调用存储的所有活动订阅函数。具有set()
方法的存储称为可写存储。
首先,让我们在 App.svelte
组件中添加以下 console.log()
语句,以查看 todos
存储及其内容的实际情况。在 todos
数组下方添加这些行
console.log("todos store - todos:", todos);
console.log("todos store content - $todos:", $todos);
现在运行应用程序时,您将在 Web 控制台中看到如下内容
如您所见,我们的存储只是一个包含 subscribe()
、set()
和 update()
方法的对象,$todos
是我们的待办事项数组。
仅供参考,以下是从头开始实现的基本工作存储
export const writable = (initial_value = 0) => {
let value = initial_value; // content of the store
let subs = []; // subscriber's handlers
const subscribe = (handler) => {
subs = [...subs, handler]; // add handler to the array of subscribers
handler(value); // call handler with current value
return () => (subs = subs.filter((sub) => sub !== handler)); // return unsubscribe function
};
const set = (new_value) => {
if (value === new_value) return; // same value, exit
value = new_value; // update value
subs.forEach((sub) => sub(value)); // update subscribers
};
const update = (update_fn) => set(update_fn(value)); // update function
return { subscribe, set, update }; // store contract
};
在这里,我们声明了 subs
,它是一个订阅者数组。在 subscribe()
方法中,我们将处理程序添加到 subs
数组中,并返回一个函数,该函数在执行时将从数组中移除处理程序。
当我们调用 set()
时,我们将更新存储的值并调用每个处理程序,并将新值作为参数传递。
通常,您不会从头开始实现存储;而是使用可写存储来创建 自定义存储,并带有特定于域的逻辑。在以下示例中,我们创建了一个计数器存储,它只允许我们将计数器加一或重置其值
import { writable } from "svelte/store";
function myStore() {
const { subscribe, set, update } = writable(0);
return {
subscribe,
addOne: () => update((n) => n + 1),
reset: () => set(0),
};
}
如果我们的待办事项列表应用程序变得过于复杂,我们可以让我们的待办事项存储处理所有状态修改。我们可以将修改 todo
数组的所有方法(如 addTodo()
、removeTodo()
等)从 Todos
组件移动到存储中。如果您有一个所有状态修改都应用于其中的中心位置,则组件只需调用这些方法来修改应用程序的状态并以反应方式显示存储公开的信息。拥有一个处理状态修改的唯一位置,可以更容易地推断状态流并发现问题。
Svelte 不会强迫您以特定方式组织状态管理;它只是为您提供工具来选择如何处理它。
实现我们的自定义待办事项存储
我们的待办事项列表应用程序并不是特别复杂,因此我们不会将所有修改方法移动到中心位置。我们将保留它们原样,而是专注于持久化我们的待办事项。
注意:如果您正在遵循本指南并从 Svelte REPL 中工作,则将无法完成此步骤。出于安全原因,Svelte REPL 在沙盒环境中工作,该环境不允许您访问 Web 存储,并且您将收到“操作不安全”错误。为了遵循本节,您需要克隆存储库并转到 mdn-svelte-tutorial/06-stores
文件夹,或者您可以使用 npx degit opensas/mdn-svelte-tutorial/06-stores
直接下载文件夹的内容。
要实现一个将内容保存到 Web 存储的自定义存储,我们需要一个可写存储来执行以下操作
- 最初从 Web 存储读取值,如果值不存在,则使用默认值进行初始化
- 每当值被修改时,更新存储本身以及本地存储中的数据
此外,由于 Web 存储仅支持保存字符串值,因此在保存时我们将不得不从对象转换为字符串,反之亦然,当我们从本地存储加载值时。
- 在
src
目录中创建一个名为localStore.js
的新文件。 - 赋予它以下内容js
import { writable } from "svelte/store"; export const localStore = (key, initial) => { // receives the key of the local storage and an initial value const toString = (value) => JSON.stringify(value, null, 2); // helper function const toObj = JSON.parse; // helper function if (localStorage.getItem(key) === null) { // item not present in local storage localStorage.setItem(key, toString(initial)); // initialize local storage with initial value } const saved = toObj(localStorage.getItem(key)); // convert to object const { subscribe, set, update } = writable(saved); // create the underlying writable store return { subscribe, set: (value) => { localStorage.setItem(key, toString(value)); // save also to local storage as a string return set(value); }, update, }; };
- 我们的
localStore
将是一个函数,在执行时最初从 Web 存储读取其内容,并返回一个包含三个方法的对象:subscribe()
、set()
和update()
。 - 当我们创建一个新的
localStore
时,我们将不得不指定 Web 存储的键和初始值。然后,我们检查该值是否在 Web 存储中存在,如果不存在,则创建它。 - 我们使用
localStorage.getItem(key)
和localStorage.setItem(key, value)
方法读取和写入 Web 存储中的信息,以及toString()
和toObj()
(使用JSON.parse()
)辅助函数来转换值。 - 接下来,我们将从 Web 存储接收到的字符串内容转换为对象,并将该对象保存在我们的存储中。
- 最后,每次我们更新存储的内容时,我们还会更新 Web 存储,并将值转换为字符串。
set()
方法,添加将值保存到 Web 存储的操作。其余代码主要用于初始化和转换内容。 - 我们的
- 现在,我们将从
stores.js
中使用我们的本地存储来创建我们本地持久化的待办事项存储。像这样更新stores.js
使用jsimport { writable } from "svelte/store"; import { localStore } from "./localStore.js"; export const alert = writable("Welcome to the to-do list app!"); const initialTodos = [ { id: 1, name: "Visit MDN web docs", completed: true }, { id: 2, name: "Complete the Svelte Tutorial", completed: false }, ]; export const todos = localStore("mdn-svelte-todo", initialTodos);
localStore('mdn-svelte-todo', initialTodos)
,我们正在配置存储,以便在键mdn-svelte-todo
下将数据保存到 Web 存储中。我们还设置了一些待办事项作为初始值。 - 现在让我们删除
App.svelte
中的硬编码待办事项。像这样更新其内容。我们基本上只是删除了$todos
数组和console.log()
语句svelte<script> import Todos from './components/Todos.svelte' import Alert from './components/Alert.svelte' import { todos } from './stores.js' </script> <Alert /> <Todos bind:todos={$todos} />
注意:这是为了使用我们的自定义存储而需要做的唯一更改。就我们使用哪种存储而言,
App.svelte
完全是透明的。 - 继续尝试您的应用程序。创建一些待办事项,然后关闭浏览器。您甚至可以停止 Svelte 服务器并重新启动它。重新访问 URL 后,您的待办事项仍然存在。
- 您也可以在 DevTools 控制台检查它。在 Web 控制台中,输入命令
localStorage.getItem('mdn-svelte-todo')
。对您的应用程序进行一些更改,例如按下取消选中所有按钮,然后再次检查 Web 存储内容。您将得到类似以下内容:
Svelte 存储提供了一种非常简单轻量级但功能强大的方法,可以以响应式的方式从全局数据存储中处理复杂的应用程序状态。并且由于 Svelte 编译了我们的代码,因此它可以提供$store
自动订阅语法,使我们能够像使用局部变量一样使用存储。由于存储具有最小的 API,因此创建自定义存储以抽象存储本身的内部工作原理非常简单。
加分曲目:过渡
现在让我们改变主题,做一些有趣和不同的事情:向我们的警报添加动画。Svelte 提供了一个完整的模块来定义过渡和动画,以便我们可以使我们的用户界面更具吸引力。
过渡是使用transition:fn指令应用的,并且由元素由于状态更改而进入或离开 DOM 触发。svelte/transition
模块导出七个函数:fade
、blur
、fly
、slide
、scale
、draw
和crossfade
。
让我们为我们的Alert
组件提供一个 fly transition
。我们将打开Alert.svelte
文件并从svelte/transition
模块导入fly
函数。
- 将以下
import
语句放在现有语句下方jsimport { fly } from "svelte/transition";
- 要使用它,请更新您的起始
<div>
标记,如下所示过渡也可以接收参数,如下所示svelte<div role="alert" on:click={() => visible = false} transition:fly >
svelte<div role="alert" on:click={() => visible = false} transition:fly="{{delay: 250, duration: 300, x: 0, y: -100, opacity: 0.5}}" >
注意:双花括号不是特殊的 Svelte 语法。它只是一个作为参数传递给 fly 过渡的文字 JavaScript 对象。
- 再次尝试您的应用程序,您会发现通知现在看起来更具吸引力。
注意:作为编译器,Svelte 可以通过排除未使用的功能来优化捆绑包的大小。在这种情况下,如果我们使用npm run build
将应用程序编译为生产环境,我们的public/build/bundle.js
文件大小将略小于 22 KB。如果我们删除transitions:fly
指令,Svelte 足够智能,可以意识到 fly 函数未使用,并且bundle.js
文件大小将下降到仅 18 KB。
这仅仅是冰山一角。Svelte 具有许多处理动画和过渡的选项。Svelte 还支持使用in:fn
/out:fn
指令指定在元素添加到或从 DOM 中移除时应用的不同过渡,并且它还允许您定义自定义CSS和JavaScript过渡。它还有一些缓动函数来指定随时间变化的变化率。查看缓动可视化工具以探索可用的各种缓动函数。
目前的代码
Git
要查看本文结尾处代码的状态,请访问您存储库的副本,如下所示
cd mdn-svelte-tutorial/07-next-steps
或直接下载文件夹的内容
npx degit opensas/mdn-svelte-tutorial/07-next-steps
请记住运行npm install && npm run dev
以在开发模式下启动您的应用程序。
REPL
要在 REPL 中查看代码的当前状态,请访问
https://svelte.net.cn/repl/378dd79e0dfe4486a8f10823f3813190?version=3.23.2
总结
在本文中,我们添加了两个新功能:一个Alert
组件和将todos
持久化到 Web 存储。
- 这使我们能够展示一些高级的 Svelte 技术。我们开发了
Alert
组件以展示如何使用存储实现跨组件状态管理。我们还了解了如何自动订阅存储以将其与 Svelte 反应式系统无缝集成。 - 然后我们了解了如何从头开始实现自己的存储,以及如何扩展 Svelte 的可写存储以将数据持久化到 Web 存储。
- 最后,我们了解了如何使用 Svelte
transition
指令在 DOM 元素上实现动画。
在下一篇文章中,我们将学习如何向我们的 Svelte 应用程序添加 TypeScript 支持。为了充分利用其所有功能,我们还将把整个应用程序移植到 TypeScript。