让Mavo在构建交互式Web应用程序中闪耀

Avatar of Dmitry Sharabin
Dmitry Sharabin 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200美元免费额度!

正如您从标题中猜到的那样,本教程致力于 Mavo:一种新的、易于使用的方法,只需编写HTML和CSS,即可创建复杂、反应式、持久性的Web应用程序,无需编写一行JavaScript代码,也不需要服务器后端。

我们将一起构建的应用程序是一个供外语学习者使用的抽认卡学习应用程序。它是一个功能齐全的CRUD应用程序,允许您

  • 创建、删除、更新抽认卡,并通过拖放重新排列它们。
  • 导入和导出抽认卡。
  • 评估您对抽认卡上单词的学习情况。

以下是我们完成的应用程序的样子

在本教程中,我将指导您完成构建应用程序的整个过程。

在某些步骤的末尾,我提供了一些建议,供您尝试使用Mavo——了解更多信息——并对我们正在构建的应用程序进行一些增强。

准备好了吗?让我们开始吧!😀

静态模板

查看 CodePen 上 Dmitry Sharabin (@dsharabin) 编写的
01. 静态模板

CodePen 上。

为了说明Mavo如何增强标准HTML,我们将首先创建一个纯静态HTML页面,然后使用Mavo将这个静态HTML转换为一个功能齐全的Web应用程序。

假设我们在<body>内有以下HTML代码

<header>
  <h1>Flashcards</h1>
</header>

<main>
<article>
  <p>Word or phrase</p>
  <p>Translation</p>
</article>
</main>

在该代码中,<article>元素表示单个抽认卡

让我们添加一些样式,使我们的HTML看起来更像一个真正的抽认卡应用程序。您可以查看源代码并在此处进行操作 这里

Mavo入门

现在,我们只有静态模板。是时候添加功能了,这样它才能真正像抽认卡应用程序一样工作。Mavo来了!

为了使用Mavo,我们首先需要在页面<head>部分包含其JavaScript和CSS文件

<head>
  ...
  <script src="https://get.mavo.io/mavo.min.js"></script>
  <link rel="stylesheet" href="https://get.mavo.io/mavo.css">
  ...
</head>

定义Mavo应用程序

要在HTML结构上启用Mavo功能,我们必须在包含Mavo应用程序的元素上使用mv-app属性(甚至可以是<body><html>元素,这都没问题!)。其值为应用程序的ID,在页面中应唯一。

考虑到<main>元素代表我们的Mavo应用程序,让我们向其添加mv-app属性,并为我们的应用程序指定ID“flashcards”

<main mv-app="flashcards">
  ...
</main>

property属性

查看 CodePen 上 Dmitry Sharabin (@dsharabin) 编写的
02. property属性

CodePen 上。

是时候告诉Mavo哪些应用程序元素重要,即我们希望哪些元素可编辑并保存。

现在我们有两个这样的元素,它们是<p>元素。让我们向这些元素添加property属性,以告诉Mavo它们包含数据。具有property属性的元素称为属性

请记住,property属性的值应描述元素,类似于idclass属性

...

  <p property="source">Word or phrase</p>
  <p property="translation">Translation</p>

...

您注意到我们应用程序中的任何变化了吗?Mavo工具栏(带有一个编辑按钮)出现在页面顶部。编辑按钮允许用户在阅读和编辑模式之间切换。现在我们的应用程序处于阅读模式。这意味着我们无法编辑页面上的数据。

现在让我们通过点击编辑按钮切换到编辑模式。发生了什么变化?编辑按钮的文本变为正在编辑,以指示我们处于编辑模式。如果您将鼠标悬停在段落上,Mavo会通过将它们突出显示为黄色来传达您可以点击以编辑它们的信息。继续吧!点击文本并编辑它。哇!我们可以直接在页面上更改内容!

💻 让我们动手实践!

假设除了单词及其翻译之外,抽认卡还应包含单词在句子中用法的示例。通过向抽认卡添加相应的元素来增强应用程序。

mv-multiple属性

查看 CodePen 上 Dmitry Sharabin (@dsharabin) 编写的
03. mv-multiple属性

CodePen 上。

此时,我们的应用程序中只有一个抽认卡。这不太有用!对于一个有效的抽认卡应用程序,我们需要能够添加、删除和重新排列抽认卡。我们该怎么做?我们可以通过向代码中添加更多<article>元素来创建更多抽认卡,但然后最终用户如何创建和删除抽认卡呢?

幸运的是,Mavo提供了一些使这变得轻而易举的东西:mv-multiple属性,它告诉Mavo某些元素可以复制。它将它所使用的元素转换为可编辑的项目集合,并生成(可自定义的)UI用于添加、删除和重新排列项目。

让我们在我们的应用程序中使用mv-multiple属性将我们孤独的抽认卡转换为抽认卡集合

<article property="flashcard" mv-multiple>
  ...    
</article>

现在将应用程序切换到编辑模式。请注意,在抽认卡下方,现在有一个添加抽认卡按钮。让我们试一试:使用该按钮创建几个抽认卡。现在我们可以在应用程序中动态添加新元素,即使HTML中没有相应的元素。但这还不是全部!

尝试将鼠标悬停在抽认卡上,并注意其右上角附近出现的三个按钮,用于通过拖放句柄添加删除重新排列元素。并且通过将鼠标悬停在任何项目栏按钮上,我们可以理解它们对应哪个抽认卡:Mavo会突出显示它。难道不神奇吗?

mv-storage 属性

查看 CodePen
04. mv-storage 属性
by Dmitry Sharabin (@dsharabin)
CodePen 上。

现在我们已经有了基本的UI,让我们尝试以下操作

  • 切换到编辑模式(如果您还没有这样做)。
  • 编辑第一张抽认卡的源单词和翻译。 也添加几个更多的抽认卡。
  • 将应用程序切换回阅读模式。
  • 最后……刷新页面。

什么?!我们的数据去哪里了?Mavo 不是应该保存它吗?发生了什么事?

实际上,我们从未告诉 Mavo是否在哪里存储我们的数据!

为此,我们需要使用mv-storage 属性。 我们有什么选择?好吧,Mavo 为我们打开了巨大的可能性,而Mavo 插件则打开了更多可能性!

在我们的应用程序中,我们将数据存储在浏览器的localStorage中,这是可用的一些最简单的选项之一,因此它非常适合我们的第一个 Mavo 应用程序。 我们只需要将属性mv-storage与值local一起添加到具有mv-app属性的元素(也称为Mavo 根)中。

<main mv-app="flashcards" mv-storage="local">
  ...
</main>

看看 Mavo 工具栏。 注意到什么了吗? 另一个按钮出现了——保存按钮。

尝试再次编辑应用程序数据。 请注意,保存按钮现在已突出显示。 将鼠标悬停在保存按钮上,Mavo 将突出显示具有未保存数据的属性。 这不是很酷吗?

单击保存按钮并刷新页面(在刷新页面之前无需切换到阅读模式)。 您的数据还在吗? 太棒了! 我们离目标又近了一步——一个功能齐全的抽认卡应用程序。

mv-autosave 属性

现在每次需要保存数据时,我们都必须单击保存按钮? 这可能更安全,以防止破坏宝贵的数据,但它通常会带来不便。 我们能否自动保存数据? 当然! 为了在每次更改数据时自动保存数据,我们可以在 Mavo 根上使用mv-autosave 属性。 其值是节流保存的时间(秒)。 让我们将mv-autosave="3"添加到应用程序的根元素中

<main mv-app="flashcard" mv-storage="local" mv-autosave="3">
  ...
</main>

再次更改数据并查看保存按钮。 看到了吗? 一开始它被突出显示,但在 3 秒后——它没有被突出显示。 我们所有的数据现在都自动保存了!

因此,现在我们应用程序的主要部分看起来像这样

<main mv-app="flashcards" mv-storage="local" mv-autosave="3">
  <article property="flashcard" mv-multiple>
    <p property="source">Word or phrase</p>
    <p property="translation">Translation</p>
  </article>
</main>
💻 让我们动手实践!

我们几乎完成了应用程序的 alpha 版本。 现在轮到您让应用程序变得更好。 不用担心,您拥有所有必要的知识。

增强应用程序,以便抽认卡可以由最终用户组织到与各种主题相关的不同组中,例如,用户可以将所有与服装相关的抽认卡收集到一个组中,将所有与厨房用具相关的抽认卡收集到另一个组中,等等。

💡 提示!

有很多方法可以实现这个目标,由您决定遵循哪种方法。 但是,在继续之前,我想让您考虑一些问题

  1. 您将使用哪个 HTML 元素作为分组元素? 如果用户可以看到抽认卡组的名称(主题名称)并可以将组折叠到标题,那将很方便。
  2. 您将向该元素添加哪些 Mavo 属性(如果有)? 该元素将是属性还是集合
  3. 最终用户能否添加新主题、删除和重新排列主题、更改主题标题以及在不同主题之间移动抽认卡?

如果您决定不将抽认卡组织成组,而是仅仅用对应于各种主题的标签标记它们怎么办? 嗯,这完全没问题。 使用标签的解决方案也适用。 为了练习,也尝试完成这种方法。

mv-bar 属性

查看 CodePen
05. mv-bar 属性
by Dmitry Sharabin (@dsharabin)
CodePen 上。

由于我们的应用程序在本地存储数据,因此默认情况下,应用程序的用户将无法与其他用户共享他们的卡片。 如果我们让他们导出他们的抽认卡并导入其他人的抽认卡,那不是很好吗? 谢天谢地,这些功能已经在 Mavo 中实现,我们可以非常轻松地将它们添加到我们的应用程序中!

mv-bar 属性控制将显示在工具栏中的按钮(如果有)。 它通常在 Mavo 根(具有mv-app属性的元素)上指定。 按钮由其 ID(非常逻辑)表示:editimportexport等。

因为我们只想向默认集添加几个按钮,所以我们可以使用所谓的相对语法,它允许我们向默认集添加和删除按钮,而无需显式列出所有内容。 我们只需要以with关键字开头mv-bar属性的值即可。

通过这样做,我们将得到以下内容

<main mv-app="flashcards"
      mv-storage="local"
      mv-autosave="3"
      mv-bar="with import export">
      ...
</main>
💻 让我们动手实践!

试用一下这些功能:添加一些抽认卡,尝试将它们导出到文件中。 然后删除现有的抽认卡并从之前导出的文件中导入抽认卡。

表达式和 MavoScript

查看 CodePen
06. 表达式和 MavoScript
by Dmitry Sharabin (@dsharabin)
CodePen 上。

现在让我们为我们的应用程序添加一些统计信息,例如抽认卡的数量! 听起来很有趣吗? 我希望如此。😀

为此,我们需要了解一些关于 Mavo 的新知识。

我们可以动态地引用我们在Mavo 应用程序中定义的任何属性的值(包括 HTML 属性中),方法是在括号内放置其名称,如下所示:[propertyName]。 这是一个简单表达式的示例,它允许我们动态计算事物,并在它们发生变化时做出反应。

现在让我们进行实验,并在flashcard属性内部添加[source]表达式,例如,在两个属性之间:sourcetranslation

...
  <p property="source">Word or phrase</p>
  [source]
  <p property="translation">Translation</p>
...

我们的应用程序发生了什么变化? 抽认卡source属性的值现在在页面上显示了两次。

切换到编辑模式并尝试更改source属性的值。 你看到了吗? 在您更改属性值时,页面内容会更新! 这就是我之前说 Mavo 允许我们开发响应式 Web 应用程序的原因。

这确实很酷,但不幸的是,在我们的例子中,它并没有什么用处:我们无法使用此表达式来计算抽认卡的数量——我们总是只会得到一个值

如果我们将[source]表达式放在flashcard属性外部会怎样? 我们将得到类似这样的东西

...
  [source]
  <article property="flashcard" mv-multiple>
    ...
  </article>
...

这与之前的情况有什么不同? 若要查看差异,请添加一些抽认卡(如果您还没有添加)。 现在,我们不再是一个值,而是一个逗号分隔值的列表所有抽认卡source属性。 这正是我们正在寻找的:列表中项目的数量对应于应用程序中抽认卡的数量。

有道理吗? 嗯,是的,但如果我们计算抽认卡的数量,而不是其source属性的值的数量,会不会更有逻辑? 毕竟,即使在我们填写其来源或翻译之前,添加的抽认卡也存在。 我建议您执行以下操作:让我们用[flashcard]替换[source]表达式

...
  [flashcard]
  <article property="flashcard" mv-multiple>
    ...
  </article>
...

注意到区别了吗? 我们仍然有一个列表,但其值不是简单值,而是对象,即包含与每个抽认卡相关的所有数据的复杂值。 好消息是这些对象的数量等于抽认卡的数量,因为每个抽认卡都有一个,即使它完全为空。 因此,现在我们每个抽认卡都有一个对象,但我们如何计算它们并显示总数呢?

现在让我们熟悉MavoScript 函数,并找到可以让我们计算抽认卡数量的函数。 请记住,我们有一个抽认卡的列表,因此我们需要找到一个可以让我们计算列表中项目数量的函数。 这里就是——count() 函数正是这样做的!

但是我们如何在表达式中使用函数呢?是否有我们需要知道的规则?当然,有一些。

  1. 表达式用括号表示。
  2. 不要嵌套括号。

让我们尝试使用count()函数来计算闪卡的数量。

...
[count(flashcard)]
<article property="flashcard" mv-multiple>
  ...
</article>
...

这正是我们想要达到的目标——现在我们的应用中有一些统计数据了!是不是很酷?

💻 让我们动手实践!

我希望您已经做好准备,可以继续尝试使用Mavo。

改进应用程序,以便统计数据不仅显示应用程序中闪卡的总数,而且还显示每个主题中闪卡的数量(如果有主题)。

💡提示!
想要根据某些条件过滤列表?where 运算符将提供帮助。

自我评估功能

我们已经有一个应用程序,可以让我们创建、编辑和存储多个闪卡。但是我们如何跟踪哪些闪卡我们已经学习过,哪些闪卡我们需要更多练习呢?任何值得尊敬的闪卡应用程序都需要一个自我评估功能。让我们研究一下如何添加它!

假设在我们的应用程序中,我们有两个用于自我评估的按钮:BadGood。每次最终用户点击按钮时,我们到底希望发生什么?嗯,这个想法相当简单。

  • 点击Bad 按钮表示用户尚未学习该单词,我们希望我们的应用程序将相应的闪卡移动到列表的开头,以便他们可以在启动应用程序后尽快看到它。
  • 点击Good 按钮表示用户已经学习了该单词,相应的闪卡需要移动到列表的末尾,让他们使用其他尚未学习的闪卡。

“您确定我们可以在没有 JavaScript 的情况下做到这一点吗?” 您可能会问。是的!Mavo 非常强大,能够为我们提供所需的所有工具!

现在我们知道了要实现什么,让我们首先设置 UI,然后继续下一步。我们的标记看起来像这样

...
<article property="flashcard" mv-multiple>
  ...
  <section>
    <h2>Evaluate Yourself</h2>
    <button>Bad</button>
    <button>Good</button>
  </section>
</article>
...

mv-action 属性

查看 Dmitry Sharabin 的 CodePen
07. mv-action 属性
(@dsharabin)
CodePen 上。

Mavo 操作允许我们创建我们自己的控件,当用户与它们交互时,以自定义方式修改数据。听起来很有希望,对吧?

要定义自定义操作,我们需要在 Mavo 应用程序内的适当元素上使用mv-action 属性。每次点击该元素时都会执行该操作。这正是我们想要的。

mv-action 属性的值是一个表达式。我们可以使用 MavoScript 提供给我们的任何表达式函数和语法,以及一些其他函数来促进数据操作,例如add()set()move()delete()。需要注意的是,与以响应方式计算的普通表达式不同,这些表达式仅在每次触发操作时计算。

因此,我们需要在集合中移动闪卡,Mavo 有一个合适的函数可以让我们做到这一点——move() 函数。它的第一个参数指的是我们要移动的项目,第二个参数是它在集合中的位置。请记住,集合的元素从0开始编号。

让我们实现我们之前讨论的大纲的第一点:在进行自我评估时,最终用户点击Bad 按钮,相应的闪卡移动到集合的开头,即成为第一个。所以在代码中,我们有

...
<article property="flashcard" mv-multiple>
  ...
  <button mv-action="move(flashcard, 0)">Bad</button>
  ...
</article>
...

请注意,在mv-action 属性中,我们引用了属性内部flashcard 属性,因为我们希望处理当前的闪卡

如果我们尝试实现大纲的第二点,我们将面临一个问题。您能否建议具体是什么问题?

让我们记住,如果最终用户点击Good 按钮,相应的闪卡将移动到集合的末尾,即成为最后一个。要使闪卡成为集合中的最后一个,我们需要知道集合中的项目数量。

谢天谢地,我们之前已经解决了这个任务并实现了相应的功能。但是我们能否使用该解决方案来解决我们当前的问题?不幸的是,我们不能:正如我们所知,我们只能在闪卡属性外部通过其名称引用闪卡的集合(并评估其大小)。但在我们的例子中,我们需要在内部进行操作:我们需要为其编写表达式的Good 按钮位于flashcard 属性内部。

那我们该怎么办?我很高兴您提出这个问题。Mavo 有解决方案。

使用 meta 元素保存中间值

查看 Dmitry Sharabin 的 CodePen
08. <meta> 元素
(@dsharabin)
CodePen 上。

因此,一方面,我们知道[count(flashcards)] 表达式如果在闪卡属性外部计算,则会给出闪卡的数量。另一方面,我们需要在flashcard 属性内部使用该值。

为了解决这个难题,我们需要在flashcard 属性外部计算闪卡的数量,并以某种方式保存结果以便能够在应用程序的其他地方使用它,准确地说是在flashcard 属性内部。对于这种情况,在 Mavo 中,有所谓的计算属性

为了保存中间结果以便我们可以引用它,我们需要在代码中使用一个 HTML 元素。建议为此目的使用<meta> 元素,如下所示:<meta property="propertyName" content="[expression]">。使用此元素的优点是它在编辑模式之外是隐藏的,在语义上和视觉上都是。

现在让我们在我们的应用程序中添加flashcardCount 计算属性。请记住,我们必须将其放在flashcard 属性外部,但之后我们可以从任何地方引用它。

...
<meta property="flashcardCount" content="[count(flashcard)]">
<article property="flashcard" mv-multiple>
    ...
</article>
...

只差一步就可以完成自我评估功能的实现:如果最终用户点击Good 按钮,相应的闪卡将移动到集合的末尾,即成为最后一个。让我们在应用程序的代码中添加相关的操作。

...
<meta property="flashcardCount" content="[count(flashcard)]">
<article property="flashcard" mv-multiple>
  ...
  <button mv-action="move(flashcard, flashcardCount)">Good</button>
</article>
...

我们完成了!恭喜!😀

💻 让我们动手实践!

还有另一种解决此任务的方法:借助$all 特殊属性。如果$all 属性位于集合内部,则它表示集合本身。因此,在这种情况下不需要使用任何计算属性。尝试自己实现该解决方案。

只剩下最后一件事需要我们修复。还记得我们在应用程序中添加一些统计数据的部分吗?还记得我们构建用于评估应用程序中闪卡数量的表达式:[count(flashcard)] 吗?相反,我们现在可以(也应该)使用我们定义的计算属性。对应用程序进行相应的更改。

要点

那么到目前为止我们学到了什么?让我们回顾一下。为了将任何静态 HTML 页面转换为 Mavo 应用程序,我们需要

  1. 在页面的<head> 部分包含 Mavo JavaScript 和 CSS 文件。
  2. mv-app 属性添加到 Mavo 根元素。
  3. 通过向它们添加property 属性来告诉 Mavo 应用程序的哪些元素很重要
  4. mv-multiple 属性放在将被复制并转换为集合的元素上。
  5. 通过向 Mavo 根添加mv-storage 属性来告诉 Mavo 在哪里存储我们的数据。
  6. 决定 Mavo 是否应该自动保存我们的数据。如果是,则向 Mavo 根添加mv-autosave 属性。

    我们还知道

  7. Mavo 工具栏是完全可自定义的。mv-bar 属性控制哪些按钮将出现在那里。
  8. 表达式允许我们在其他元素中显示属性的当前值并执行计算。表达式的值(和类型)取决于表达式在代码中所处的位置。Mavo 的表达式语法称为 MavoScript。
  9. 自定义操作允许创建以自定义方式修改数据的控件。要定义自定义操作,请在 Mavo 应用程序内的适当元素上设置mv-action 属性。
  10. 值为表达式的属性称为计算属性。为了保存中间结果以便能够在应用程序的其他地方引用它,建议使用<meta> 元素。

结语替代

所以我们构建了我们的应用。它已经是完美的了吗?当然不是,没有什么东西是完美的!还有很多东西可以改进,还有很多功能可以添加(借助 Mavo,我们甚至可以使我们的应用多语言化!)。继续,进一步增强它,不要犹豫尝试一些新东西!

到目前为止,我们了解到的关于 Mavo 的知识仅仅是冰山一角,还有很多东西需要学习。我鼓励你仔细研究一下,阅读文档,查看示例(在Mavo 网站上,或在 CodePen 上:由Lea Verou制作和一些由我自己制作的),并创建新的东西!祝你好运!😉

致谢

我要感谢两位很棒的人。首先,我要衷心感谢Lea Verou,她不仅激励我撰写本教程(并帮助我实现了它),而且一直激励着我,她让网页开发的世界变得更美好。我从未见过如此有天赋的人,我很高兴有机会和她一起做一些事情!

我也要感谢James Moore。他在 Udemy 上的课程“JavaScript 初学者函数式编程”中使用的示例促使我制作了自己的抽认卡学习应用版本。他是一位很棒的老师!