让Mavo在构建交互式Web应用程序中发光
本教程将深入探讨Mavo:一种便捷易用的方法,只需编写HTML和CSS,无需一行JavaScript代码或服务器后端,即可创建复杂、响应式、持久化的Web应用程序。
Mavo由麻省理工学院CSAIL的Haystack小组开发,由Lea Verou领导。
我们将共同构建一个外语学习用的抽认卡应用程序。这是一个功能齐全的CRUD应用程序,允许您:
- 创建、删除、更新抽认卡,并通过拖放重新排列。
- 导入和导出抽认卡。
- 评估您对抽认卡上单词的掌握程度。
以下是我们完成的应用程序的外观:
在本教程中,我将引导您完成构建应用程序的整个过程。
在某些步骤的结尾,我将提供一些建议,让您尝试使用Mavo——学习更多知识——并对我们正在构建的应用程序进行一些增强。
准备好了吗?让我们开始吧!?
静态模板
为了说明Mavo如何增强标准HTML,我们将首先创建一个纯静态HTML页面,然后使用Mavo将这个静态HTML转换为一个功能齐全的Web应用程序。
假设我们在以下HTML代码中:
<h1 id="抽认卡">抽认卡</h1> <main><p>单词或短语</p> <p>翻译</p> </main>
在该代码中,<main></main>
元素表示单个抽认卡。
让我们添加一些样式,使我们的HTML看起来更像一个实际的抽认卡应用程序。您可以查看源代码并在此处使用它。
开始使用Mavo
现在,我们只有静态模板。是时候添加功能了,这样它才能真正像抽认卡应用程序一样工作。Mavo闪亮登场!
为了使用Mavo,我们首先需要在页面的部分包含其JavaScript和CSS文件:
... <link href="https://get.mavo.io/mavo.css" rel="stylesheet"> ...
您可能需要支持旧版浏览器,或者希望能够阅读代码?您可以通过回答几个问题来自定义您正在使用的Mavo版本和构建版本。
定义Mavo应用程序
要在HTML结构上启用Mavo功能,我们必须在包含Mavo应用程序的元素上使用mv-app
属性(甚至可以是或
元素,都可以!)。其值为应用程序的ID,在页面中应唯一。如果我们不带值使用
mv-app
,并且同一元素上没有id
或name
属性,则会自动生成诸如mavo1
、mavo2
等名称。
但是,强烈建议您命名Mavo应用程序,因为该名称在许多地方都会使用。
考虑到<main></main>
元素代表我们的Mavo应用程序,让我们向其添加mv-app
属性,并为我们的应用程序指定ID“flashcards”:
<main mv-app="flashcards"> ... </main>
属性属性
是时候告诉Mavo哪些应用程序元素是重要的,即我们希望哪些元素可编辑并保存。
现在我们有两个这样的元素,它们是<p></p>
元素。让我们向这些元素添加property
属性,以告诉Mavo它们包含数据。具有property
属性的元素称为属性。
我们可以将property
属性放在任何HTML5元素上,Mavo知道如何使其可编辑。例如,对于<input>
,您可以编辑其内容,但<time></time>
允许您通过适当的日期/时间选择器编辑其日期/时间。
您还可以通过插件扩展这组规则,并以新的方式(例如,富文本)使元素可编辑。
请记住,property
属性的值应类似于id
或class
属性那样描述元素:
... <p property="source">单词或短语</p> <p property="translation">翻译</p> ...
如果您已经有充分描述元素的class
、id
或itemprop
属性,则可以使用不带值的property
,例如property="source"
。
您注意到我们的应用程序中有什么变化吗?页面顶部出现了一个带有编辑按钮的Mavo工具栏。编辑按钮允许用户在阅读和编辑模式之间切换。现在我们的应用程序处于阅读模式。这意味着我们无法编辑页面上的数据。
Mavo工具栏是完全可定制的,几乎所有由Mavo生成的UI都是如此:您可以更改其位置、删除其默认样式、添加自定义按钮元素或使用您自己的HTML元素,等等。
我们稍后在本教程中将看到一个这样的自定义示例。
访问Mavo网站的此部分以了解更多信息。
现在让我们通过单击编辑按钮切换到编辑模式。发生了什么变化?编辑按钮的文本变为正在编辑,以指示我们处于编辑模式。如果您将鼠标悬停在段落上,Mavo会通过将它们突出显示为黄色来告知您可以单击以编辑它们。开始吧!单击文本并编辑它。哇!我们可以直接在页面上更改内容!
假设除了单词及其翻译之外,抽认卡还应包含一个单词在句子中用法的示例。通过向抽认卡添加相应的元素来增强应用程序。
mv-multiple
属性
目前,我们的应用程序中只有一个抽认卡。这没什么用!对于一个有效的抽认卡应用程序,我们需要能够添加、删除和重新排列抽认卡。我们该如何做到这一点?我们可以通过向代码中添加更多<main></main>
元素来创建更多抽认卡,但是最终用户如何创建和删除抽认卡呢?
幸运的是,Mavo提供了一些使这变得轻而易举的东西:mv-multiple
属性,它告诉Mavo某些元素可以被复制。它将其使用的元素转换为可编辑的项目集合,并生成(可定制的)UI用于添加、删除和重新排列项目。
如果在没有property
属性的元素上使用mv-multiple
,Mavo会自动向其添加property="collection"
(或collection2
、collection3
等,以便名称唯一)。但是,建议您也使用property
属性,为您的集合命名,并确保在HTML更改时保留其数据。
让我们在我们的应用程序中使用mv-multiple
属性,将我们孤单的抽认卡转换为抽认卡集合:
... <main mv-app="flashcards" mv-multiple> <p property="source">单词或短语</p> <p property="translation">翻译</p> </main>
也可以将属性名称指定为mv-multiple
的值,例如mv-multiple="flashcards"
。
mv-multiple
属性位于将被复制的元素上,而不是集合的容器上。人们常常犯的一个错误是使用<main mv-multiple></main>
而不是<main><p mv-multiple></p></main>
,并且在检查元素或样式使其明显之前,通常会长时间不被发现。现在切换应用程序到编辑模式。请注意,在抽认卡下方,现在有一个添加抽认卡按钮。让我们试一试:使用该按钮创建一些抽认卡。现在我们可以直接在应用程序中动态添加新元素,即使HTML中没有相应的元素。但这还不是全部!
请注意,<main></main>
上的property
属性实际上并没有使整个<main></main>
元素可编辑,而是充当分组元素。当您在包含其他属性的元素上使用property
属性时,就会发生这种情况。尝试将鼠标悬停在抽认卡上,并注意出现在其右上角的三个按钮,用于通过拖放句柄添加、删除和重新排列元素。通过将鼠标悬停在任何项目栏按钮上,我们可以理解它们对应哪个抽认卡:Mavo会突出显示它。是不是很神奇?
您可以自定义Mavo生成的任何UI元素,例如,您可以通过使用mv-drag-handle
类创建您自己的拖动句柄。
Mavo添加到集合中每个项目的按钮也可以通过键盘访问。甚至重新排序:您可以将焦点放在拖动句柄上,并使用箭头键移动项目。
mv-storage
属性
现在我们已经有了基本的UI,让我们尝试以下操作:
- 切换到编辑模式(如果您尚未这样做)。
- 编辑第一个抽认卡的源单词和翻译。再添加几个抽认卡。
- 将应用程序切换回阅读模式。
- 最后……刷新页面。
什么?!我们的数据去哪儿了?Mavo不是应该保存它吗?发生了什么?
实际上,我们从未告诉Mavo是否或在哪里存储我们的数据!
为此,我们需要使用mv-storage
属性。我们有什么选择?好吧,Mavo为我们打开了巨大的可能性,而Mavo插件则打开了更多可能性!
在我们的应用程序中,我们将数据存储在浏览器的localStorage中,这是最简单的选项之一,因此它非常适合我们的第一个Mavo应用程序。我们只需要在具有mv-app
属性的元素(也称为Mavo根)上添加值为local
的mv-storage
属性。
<main mv-app="flashcards" mv-storage="local"> ... </main>
看看Mavo工具栏。注意到什么了吗?出现另一个按钮——保存按钮。
尝试再次编辑应用程序数据。请注意,保存按钮现在已突出显示。将鼠标悬停在保存按钮上,Mavo将突出显示具有未保存数据的属性。是不是很酷?
单击保存按钮并刷新页面(无需在刷新页面之前切换到阅读模式)。您的数据还在吗?太好了!我们离我们的目标又近了一步——一个功能齐全的抽认卡应用程序。
mv-autosave
属性
现在我们每次需要保存数据时都必须单击保存按钮吗?这可能更安全,可以防止破坏宝贵的数据,但这通常会很不方便。我们可以自动保存数据吗?当然!为了在每次更改数据时自动保存数据,我们可以在Mavo根元素上使用mv-autosave
属性。其值为节流保存的秒数。让我们向应用程序的根元素添加mv-autosave="3"
:
<main mv-app="flashcard" mv-autosave="3" mv-storage="local"> ... </main>
如果mv-autosave="3"
,Mavo最多每三秒钟只能保存一次。这对于保留更改历史记录的后端(例如,GitHub、Dropbox)尤其有用,以防止洪泛,这会使该历史记录毫无用处。
要禁用节流并立即保存,我们可以使用mv-autosave="0"
或仅使用mv-autosave
,这也会从UI中删除保存按钮(因为在这种情况下它没有用处)。
再次更改数据并查看保存按钮。看到了吗?一开始,它被突出显示,但在3秒钟后——它没有被突出显示。我们所有的数据现在都自动保存了!
因此,现在我们的应用程序的主要部分看起来像这样:
<main mv-app="flashcards" mv-autosave="3" mv-storage="local"><p property="source">单词或短语</p> <p property="translation">翻译</p> </main>
我们几乎完成了应用程序的alpha版本。现在轮到您使应用程序变得更好。别担心,您拥有所需的所有知识。
增强应用程序,以便抽认卡可以由最终用户组织到与各种主题相关的不同组中,例如,用户可以将所有与服装相关的抽认卡收集到一个组中,将所有与厨房用具相关的抽认卡收集到另一个组中,等等。
?提示!
有很多方法可以实现这个目标,这取决于您决定遵循什么。但是,我希望您在继续之前考虑一些问题:
- 您将使用什么HTML元素作为分组元素?如果用户可以看到抽认卡组的名称(主题名称)并可以将组折叠到标题,那将很方便。
- 您将向该元素添加哪些Mavo属性(如果有的话)?该元素将是属性还是集合?
- 最终用户能否添加新主题、删除和重新排列它们、更改主题标题以及在不同主题之间移动抽认卡?
如果您决定不按组组织抽认卡,而是只用对应于各种主题的标签标记它们,那也没问题。使用标签的解决方案也是合适的。为了练习,也尝试完成这种方法。
mv-bar
属性
由于我们的应用程序在本地存储数据,因此默认情况下,应用程序的用户将无法与其他用户共享他们的卡片。如果我们允许他们导出他们的抽认卡和导入其他人的抽认卡,那不是很好吗?谢天谢地,这些功能已经在Mavo中实现,我们可以很容易地将它们添加到我们的应用程序中!
mv-bar
属性控制将显示哪些按钮(如果有的话)在工具栏中。它通常在Mavo根(具有mv-app
属性的元素)上指定。按钮由它们的id(非常逻辑)表示:edit、import、export等。
由于我们只想向默认集合添加几个按钮,因此我们可以使用所谓的相对语法,它允许我们向默认集合添加和删除按钮,而无需显式列出所有内容。我们只需要以with
关键字开头mv-bar
属性的值即可。
通过这样做,我们将得到以下内容:
<main mv-app="flashcards" mv-autosave="3" mv-bar="with import export" mv-storage="local"> ... </main>
尝试这些功能:添加一些抽认卡,尝试将它们导出到文件中。然后删除现有抽认卡并从先前导出的文件中导入抽认卡。
表达式和MavoScript
现在让我们向我们的应用程序添加一些统计信息,例如抽认卡的数量!听起来很有趣吗?我希望如此。?
为此,我们需要学习一些关于Mavo的新知识。
我们可以动态地引用我们在任何地方定义的任何属性的值在我们的Mavo应用程序中(包括在HTML属性中),方法是将它的名称放在方括号中,像这样:[propertyName]
。这是一个简单的表达式的示例,它允许我们动态计算事物,并在它们发生变化时做出反应。
Mavo的表达式语法称为MavoScript。它类似于电子表格公式,允许我们执行计算和其他操作(使用数字、文本、列表等),但旨在更易于阅读并适应嵌套关系。您可以在文档中了解有关Mavo表达式和MavoScript的更多信息。
现在让我们尝试一下,在抽认卡属性内部添加[source]
表达式,例如,在两个属性之间:源和翻译。
... <p property="source">单词或短语</p> [source] <p property="translation">翻译</p> ...
我们的应用程序发生了什么变化?抽认卡源属性的值现在在页面上显示了两次。
切换到编辑模式并尝试更改源属性的值。您看到了吗?在您更改属性值时,页面内容会更新!这就是我之前说Mavo允许我们开发响应式Web应用程序的原因。
这确实很酷,但不幸的是,在我们的例子中,它并没有什么用处:我们无法使用此表达式来计算抽认卡的数量——我们总是只有一个值。
如果我们将[source]
表达式放在抽认卡属性外部会怎样?我们将得到如下内容:
... [source] ... ...
这与之前的案例有何不同?要查看差异,如果您尚未这样做,请添加一些抽认卡。现在,我们不是一个值,而是一个逗号分隔的值列表:所有抽认卡的源属性。这正是我们一直在寻找的:列表中项目的数量对应于应用程序中抽认卡的数量。
说得通吗?是的,但是如果我们计算抽认卡的数量,而不是其源属性的值的数量,岂不是更合乎逻辑?毕竟,即使在我们填写其源或翻译之前,添加的抽认卡也存在。我建议您执行以下操作:让我们用[flashcard]
替换[source]
表达式:
... [flashcard] ... ...
注意到区别了吗?我们仍然有一个列表,但其值不是简单值,而是对象,即包含与每个抽认卡相关的所有数据的复杂值。好消息是这些对象的数目等于抽认卡的数目,因为每个抽认卡都有一个,即使它完全为空。所以,现在我们每个抽认卡都有一个对象,但是我们如何计算它们并显示总数呢?
现在让我们熟悉MavoScript函数,并找到可以让我们计算抽认卡数量的函数。记住,我们有一个抽认卡列表,所以我们需要找到一个可以让我们计算列表中项目数量的函数。它来了——count()
函数就是这样做的!
但是我们如何在表达式中使用函数呢?我们需要注意哪些规则?是的,有几个:
- 表达式用方括号表示。
- 不要嵌套括号。
让我们尝试使用count()
函数来计算抽认卡的数量:
... [count(flashcard)] ... ...
这正是我们所追求的——现在我们的应用程序中有一些统计数据了!是不是很酷?
我希望您已经热身并准备好继续尝试Mavo了。
改进应用程序,以便不仅显示应用程序中抽认卡的总数的统计信息,而且还显示每个主题中抽认卡的数量的统计信息(如果有的话)。
?提示!
想要根据某些条件过滤列表吗?where
运算符将有所帮助。
自我评估功能
我们已经有一个应用程序,可以让我们创建、编辑和存储多个抽认卡。但是我们如何跟踪我们已经学习了哪些抽认卡,以及哪些抽认卡需要更多练习呢?任何值得尊敬的抽认卡应用程序都需要自我评估功能。让我们研究一下我们如何添加它!
假设在我们的应用程序中,我们有两个用于自我评估的按钮:差和好。我们希望每次最终用户单击按钮时会发生什么?好吧,这个想法很简单:
- 单击“差”按钮将表示用户尚未学习该单词,我们希望我们的应用程序将相应的抽认卡移动到列表的开头,以便他们可以在启动应用程序后尽快看到它。
- 单击“好”按钮将表示用户已经学习了该单词,相应的抽认卡需要移动到列表的末尾,让他们使用他们尚未学习的其他抽认卡。
“您确定我们可以在没有JavaScript的情况下做到这一点吗?” 你可能会问。是的!Mavo非常强大,能够为我们提供所需的所有工具!
现在我们知道了我们将要实现的内容,让我们首先设置UI,然后继续下一步。我们的标记看起来像这样:
... ... <h2 id="评估自己">评估自己</h2> <button>差</button> <button>好</button> ...
mv-action
属性
Mavo操作允许我们创建我们自己的控件,这些控件以自定义方式修改数据,当用户与它们交互时。听起来很有希望,对吧?
要定义自定义操作,我们需要在Mavo应用程序内的相应元素上使用mv-action
属性。每次单击该元素时都会执行该操作。这正是我们一直在寻找的。
对于<form></form>
元素,当表单提交时会执行自定义操作。mv-action
属性的值是一个表达式。我们可以使用MavoScript为我们提供的任何表达式函数和语法,以及一些其他函数来促进数据操作,例如add()
、set()
、move()
或delete()
。重要的是要注意,与以反应方式计算的普通表达式不同,这些表达式仅在每次触发操作时才计算。
Mavo期望mv-action
属性的值是一个表达式,因此无需将其括在括号中:mv-action="expression"
。此外,如果我们包含它们,它们将被视为表达式的部分。
因此,我们需要移动集合内的抽认卡,而Mavo有一个合适的函数可以让我们做到这一点——move()
函数。它的第一个参数指的是我们正在移动的项目,第二个参数是它在集合中的位置。请记住,集合的元素从0开始编号。
想要了解有关move
函数(及其变体)以及自定义操作的更多信息,请参阅文档。
让我们实现我们前面讨论的大纲的第一点:在自我评估时,最终用户单击“差”按钮,相应的抽认卡将移动到集合的开头,即成为第一个。所以在代码中,我们有:
... ... <button mv-action="move(this, 0)">差</button> ... ...
请注意,在mv-action
属性中,我们在属性内部引用抽认卡属性,因为我们希望处理当前抽认卡。
如果我们尝试实现大纲的第二点,我们将面临一个问题。您能否建议这究竟是什么问题?
让我们记住,如果最终用户单击“好”按钮,相应的抽认卡将移动到集合的末尾,即成为最后一个。为了使抽认卡成为集合中的最后一个,我们需要知道集合中项目的数量。
谢天谢地,我们之前已经解决了这项任务并实现了相应的功能。但是我们能否使用该解决方案来解决我们当前的问题呢?不幸的是,我们不能:正如我们已经知道的,我们只能在抽认卡属性外部引用抽认卡的集合(并评估其大小)。但在我们的例子中,我们需要在其中进行:我们需要为其编写表达式的“好”按钮在抽认卡属性内部。
那我们该怎么办呢?我很高兴你问。Mavo有解决方案。
使用元元素保存中间值
因此,一方面,我们知道[count(flashcards)]
表达式会在外部评估抽认卡属性时为我们提供抽认卡的数量。另一方面,我们需要在其中使用该值抽认卡属性。
为了解决这个难题,我们需要在我们的代码中评估抽认卡的数量,并以某种方式保存结果,以便能够在应用程序的其他地方使用它,准确地说是在抽认卡属性内部。对于这种情况,在Mavo中,有所谓的计算属性。
为了保存中间结果以便我们可以引用它,我们需要代码中的HTML元素。建议为此使用<meta>
元素,如下所示:<meta content="[expression]" property="propertyName">
。使用此元素的优点是它在编辑模式外部是隐藏的,在语义上和视觉上都是如此。
请记住,默认情况下不会保存计算属性。
现在让我们在我们的应用程序中添加flashcardCount
计算属性。记住,我们必须将其放在抽认卡属性外部,但是然后我们可以从任何地方引用它:
... <meta content="[count(flashcard)]" property="flashcardCount"> ... ...
只剩下一个步骤来完成自我评估功能的实现:如果最终用户单击“好”按钮,相应的抽认卡将移动到集合的末尾,即成为最后一个。让我们在应用程序的代码中添加相关的操作:
... <meta content="[count(flashcard)]" property="flashcardCount"> ... <button mv-action="move(this, flashcardCount)">好</button> ...
我们完成了!恭喜!?
还有另一种解决该任务的方法:借助$all
特殊属性。如果$all
属性位于集合内部,则它代表集合本身。因此,在这种情况下无需使用任何计算属性。尝试自己实现该解决方案。
只剩下最后一件事我们需要修复。还记得我们向应用程序添加了一些统计数据的部分吗?还记得我们构建的用于评估应用程序中抽认卡数量的表达式吗:[count(flashcard)]
?相反,我们现在可以使用(也应该使用)我们定义的计算属性。在应用程序中进行相应的更改。
结论
那么到目前为止我们学习了什么呢?让我们回顾一下。为了将任何静态HTML页面转换为Mavo应用程序,我们需要:
- 在页面的
部分包含Mavo JavaScript和CSS文件。
- 向Mavo根元素添加
mv-app
属性。 - 通过向它们添加
property
属性来告诉Mavo我们的应用程序的哪些元素是重要的。 - 将
mv-multiple
属性放在将被复制并转换为集合的元素上。 - 通过向Mavo根添加
mv-storage
属性来告诉Mavo在哪里存储我们的数据。 - 决定Mavo是否应该自动保存我们的数据。如果是,则向Mavo根添加
mv-autosave
属性。我们还知道: - Mavo工具栏是完全可定制的。
mv-bar
属性控制将显示哪些按钮。 - 表达式允许我们在其他元素中显示属性的当前值并执行计算。表达式的值(和类型)取决于表达式在代码中所处的位置。Mavo的表达式语法称为MavoScript。
- 自定义操作允许创建以自定义方式修改数据的控件。要在Mavo应用程序内的相应元素上定义自定义操作,请设置
mv-action
属性。 - 值为表达式的属性称为计算属性。为了保存中间结果以便能够在应用程序的其他地方引用它,建议使用
<meta>
元素。
后记
所以我们构建了我们的应用程序。它已经完美了吗?当然不是,没有什么东西是完美的!有很多东西可以改进,还有很多功能可以添加(在Mavo的帮助下,我们甚至可以使我们的应用程序多语言化!)。继续,进一步增强它,不要犹豫尝试新的东西!
到目前为止,我们对Mavo的了解只是冰山一角,还有很多东西。我鼓励您仔细阅读文档,检查示例(在Mavo网站上,或在CodePen上:由Lea Verou制作和一些由我自己制作),并创建新内容!祝你好运!?
致谢
我要感谢两位伟大的人。首先,我要衷心感谢Lea Verou,她不仅激励我撰写本教程(并帮助我实现它),而且还一直激励我,她使Web开发世界变得更美好。我从未见过如此有天赋的人,我很高兴有机会和她一起做一些事情!
我还感谢James Moore。他在Udemy上的“JavaScript初学者函数式编程”课程中使用的示例促使我制作我自己的抽认卡学习应用程序版本。他是一位很棒的老师!
以上是让Mavo在构建交互式Web应用程序中发光的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

您是否曾经在项目上需要一个倒计时计时器?对于这样的东西,可以自然访问插件,但实际上更多

关于Flex布局中紫色斜线区域的疑问在使用Flex布局时,你可能会遇到一些令人困惑的现象,比如在开发者工具(d...

在元素个数不固定的情况下如何通过CSS选择第一个指定类名的子元素在处理HTML结构时,常常会遇到元素个数不�...
