Mustache.js

作者: Reuven Lerner

在之前的文章中,我已经研究了 JavaScript 在服务器端和客户端的多种用途。我希望在未来几个月继续探索此类系统,尤其是在客户端。

但现在,我想解决 JavaScript 程序员一直遇到的一个更平凡的问题:JavaScript 没有任何原生的字符串插值机制。当然,您总是可以使用 + 运算符来连接字符串


"hello, " + "world"

这会给你字符串


"hello, world"

这正是您可能期望的结果。但是,如果您有一个变量 "username",并且想以友好的方式对用户说 "hello" 怎么办?在 Ruby 中,您会使用


"hello, #{username}"

在 Python 中,您会写


"hello, %s" % username

但是在 JavaScript 中,您基本上只能输入


"hello, " + username

如果您在字符串末尾只有一个变量,这还算不错。但是,我使用 JavaScript 越多,我就越希望拥有更复杂的字符串插值。

当我在许愿时,我希望拥有我从其他语言或各种 Web 框架中习惯的所有类型的文本格式化和模板功能。

现在,这听起来并不难。天知道,这些年来我使用了很多模板系统,所以我知道创建一个并不难——尤其是当标准不高时。但是,随着 Web 应用程序越来越侧重于浏览器和 JavaScript,您将需要一个模板解决方案,使您能够在该环境中轻松工作。

幸运的是,存在几种模板系统。其中最突出和最有趣的是 Mustache.js,它是 Mustache 模板系统的 JavaScript 实现,可用于多种不同的语言。与我使用过的大多数其他模板不同,Mustache.js 不是一个完全成熟的编程语言,正如您可能期望的那样。相反,它是一种严格定义的领域特定语言,用于描述页面,但不会使模板变成另一个代码存储库。

因此,本文探讨了 Mustache.js——如何安装和使用它,以及何时适合使用它,以及如何使用它的一些更高级的功能。

模板

许多读者可能熟悉典型的模板类型,其风格被 PHP、ASP、JSP 和 Ruby 的 ERb 使用。任何应该执行的内容都放在看起来像这样的花括号中


<% varname = 5 %>

并且,任何您想在屏幕上显示的内容都使用几乎相同类型的标签,但左侧有一个 = 符号


<%= varname %>

这种模板的好处是它们相当容易使用。您不必担心哪些符号表示什么,或者设置一个文件只是为了查看一些插值变量。但另一方面,它们对于生成大规模报告和进行严肃的文本操作来说太简单了。

另一个问题是,一旦您将代码放入模板中,您就违反了 MVC 规则,即您不希望在模板中放入太多可执行代码。分配变量不是一个好主意,但是调用方法,更不用说从数据库中检索行,是您通常不希望在视图中做的事情。但是,您可以更严格地解释这种不执行策略。如果您可以避免所有可执行代码,包括 if/then 语句、循环以及您习惯的其他内容,那会怎么样?

Mustache 采用了这种理念,因为它允许在模板中进行有限的操作。您可能会争辩(我也可能会相信您),说 Mustache 的口号所说的“无逻辑模板”太过分了。实际上,Mustache 模板确实包含相当多的逻辑。但是模板语言的性质确保了特殊功能不会被滥用得太厉害。如果您想执行代码,您必须在 Mustache 领域之外执行。

(如果您想知道,它之所以被称为 Mustache 是因为它使用双花括号 {{ 和 }} 作为分隔符。双花括号表示您希望进行插值的位置,并且它们还分隔各种控制结构。)

安装 Mustache.js 再简单不过了。从 GitHub 下载单个 mustache.js 文件,将其放在 Web 应用程序的 JavaScript 目录中的适当目录中——或者如果您只是在框架之外进行实验,则放在 HTML 文件旁边——您就可以开始了。

请注意,包含 Mustache.js 不会将您的 HTML 文件(或 JavaScript 文件,就此而言)变成 Mustache 模板。相反,它为您提供了许多可以应用于文本字符串的函数。然后,您可以对这些文本字符串执行任何您想执行的操作,从将它们插入文件到将它们用于进一步处理。

列表 1 包含一个使用 Mustache.js 的简单示例。在 <head> 部分的顶部,我同时包含了 jQuery 库和 Mustache.js,这通常是我在 HTML 文件中会做的。然后我有一段 JavaScript 代码在标准的 $(document).ready 函数调用中执行,确保它只在 jQuery 检测到整个 HTML 文档已加载后执行。这避免了竞争条件,在这种情况下,JavaScript 可能会或可能不会在 HTML 呈现之前运行。

列表 1. Mustache 的简单用法

<!DOCTYPE html>
<html>
<head>
<h3>Testing</h3>

<script src="jquery.js"></script>
<script type="text/javascript" src="mustache.js"></script>

<script type="text/javascript">
     $(document).ready(
     function () {
     var template_vars = {
     name: 'Reuven',
     number_of_children: 3
     }

     var template = "<b>{{name}}</b> has 
     ↪{{number_of_children}} children.";
     var html = Mustache.to_html(template, template_vars);
     $('#target').html(html);
     });
</script>

</head>
<body>
<h1>Testing testing</h1>
<p>This is a paragraph</p>
<p id="target">This space for rent</p>
</body>
</html>

然后我定义一个变量 (template_vars),一个 JavaScript 对象,具有两个属性,“name”和“number_of_children”。这些属性可以是任何数据类型,包括函数。如果属性是一个函数,则在插值时对其进行评估,并将函数评估的结果插入到模板中。

然后我将插值分解为三个不同的部分。首先,我定义了要插入变量的文本(“template”变量)。请注意字符串是如何成为一个小模板的,并且 {{ }}(双花括号)中的任何内容都将被 Mustache.js 评估为变量。

接下来,将您的 template_vars 应用于模板,获取一些 HTML 返回。然后您可以对该 HTML 执行任何您想执行的操作,包括(最容易地)替换现有 HTML 标签中的文本。您还可以创建一个新节点,替换现有节点或进一步修改文本。

最后,我做了一些相当简单的事情,即使用 jQuery 的“html”函数将现有 HTML 替换为改进的版本。

对于更复杂的东西,它更像传统的 HTML 模板,请考虑列表 2。在此示例中,我决定进行类似于疯狂填词的替换,但我没有更改字符串中的文本,而是在文档本身中更改了它。使用 jQuery 的选择器,我选择了所有带有“template”类的元素。(这允许页面的作者决定是否在特定标签上使用 {{ }} 标签。)

列表 2. 替换模板中的文本

<!DOCTYPE html>
<html>
<head>
<h3>Testing</h3>

<script src="jquery.js"></script>
<script type="text/javascript" src="mustache.js"></script>

<script type="text/javascript">
     $(document).ready(
     function () {
     var template_vars = {
     proper_noun: 'Reuven',
     color: 'green',
     food: 'ice cream'
     }

     $(".template").each(function(index, value) {
     var current_html = $(this).html();
     var translated = Mustache.to_html(current_html, template_vars);
     $(this).html(translated);
     });

     });
</script>

</head>
<body>
<h1>Testing testing</h1>
<p>This is a paragraph</p>
<p class="template">My name is {{proper_noun}}.</p>
<p class="template">I like to wear {{color}} shirts, 
and eat {{food}} for breakfast.</p>
</body>
</html>

这段代码中最有趣和最重要的部分可能是我用来进行翻译的回调函数。我没有使用典型的 jQuery 循环(它会变成一个代码的乱麻),而是决定使用“each”函数,它迭代一个集合。在每次迭代中,$(this) 指的是该项,然后您使用 Mustache.to_html 函数来翻译它,然后用其转换后的自身替换文本。通过这种方式,您的 JavaScript 可以轻松地影响页面上的文本。

如果您要求 Mustache 使用您未定义的变量值会发生什么?它会静默继续,使用空字符串。这意味着如果您的 template_vars 变量包含一个或多个拼写错误的键,您将不会收到任何警告。

循环和条件语句

还记得我写过我不会称 Mustache.js 为“无逻辑模板”,因为模板语言仍然包含条件语句吗?好吧,现在您可以看到我的意思了。(我应该补充一点,我相当确信我通常不希望在模板中评估/执行代码。但是,条件语句和循环是我拥有的每个有用的模板系统都包含的两件事,它们是模板有用的必要逻辑。)

如果您查看列表 3,您将看到如何创建循环。我在我的 template_vars 变量中添加了一个数组 (“children”)。但是,您不是说 {{children}} 来检索数组的内容,而是说循环开始时使用 {{#children},循环结束时使用 {{/children}。Mustache.js 非常聪明,知道该怎么做,它会重复这些分隔符之间的块,数组的每个元素重复一次。要获取当前的数组元素本身,您可以使用特殊语法 {{.}}。

列表 3. 循环

<!DOCTYPE html>
<html>
<head>
<h3>Testing</h3>

<script src="jquery.js"></script>
<script type="text/javascript" src="mustache.js"></script>

<script type="text/javascript">
     $(document).ready(
     function () {
     var template_vars = {
     name: 'Reuven',
     children: ['Atara', 'Shikma', 'Amotz']
     }

     var template = "<b>{{name}}</b> has children
     ↪named:<ul>{{#children}}<li>{{.}}</li>{{/children}}.</ul>";
     var html = Mustache.to_html(template, template_vars);
     $('#target').html(html);
     });
</script>


</head>
<body>
<h1>Testing testing</h1>
<p>This is a paragraph</p>
<p id="target">This space for rent</p>
</body>
</html>

这当然是某种程度的逻辑,但与 {{#mycondition}} 标签相比,这不算什么,它开始了等效于 if-then 语句的结构。但是等等,您要检查什么?好吧,如果您以 {{#mycondition}} 开始您的条件语句,这意味着您将把“mycondition”视为一个函数,在运行时对其进行评估,然后仅显示块的内容(即,{{#mycondition}}{{/#mycondition}} 之间的内容,如果该函数返回“true”)。

Mustache 还有许多其他功能。默认情况下,它会自动转义 HTML,但它有一种机制 {{{ }}},它使用原始 HTML,而无需清理可能既烦人又具有潜在危险的 < 和 > 符号。因此,您可以灵活地在应用程序中适当地替换文本。

我提供的示例显然有些牵强和简单。幸运的是,Mustache.js 的语法足够简单,应该不需要很长时间才能将其合并到您的工作中。

结论

Mustache 是一个简单但功能强大的 JavaScript 模板系统。如果您开始构建一个 Web 应用程序,该应用程序需要根据 AJAX 调用或 JavaScript 输出重写部分文本,或者如果您正在编写一个基于 JavaScript 的单页应用程序,那么您肯定应该研究 Mustache.js。GitHub 上的主页有很好的文档,其他教程和文档也从那里链接。

资源

Mustache 的主页是 http://mustache.github.com

有关 Mustache.js 的有趣分析,以及可以改进的地方(以及 fork 的描述),请阅读 Yehuda Katz 的博客文章,网址为 http://yehudakatz.com/2010/09/09/announcing-handlebars-js

Mustache 照片,来自 Shutterstock.com

Reuven M. Lerner 是一位长期从事 Web 开发的开发者,提供 Python、Git、PostgreSQL 和数据科学方面的培训和咨询服务。他撰写了两本编程电子书(Practice Makes Python 和 Practice Makes Regexp),并为程序员发布免费的每周新闻通讯,网址为 http://lerner.co.il/newsletter。Reuven 在 Twitter 上的账号是 @reuvenmlerner,与妻子和三个孩子住在以色列的莫迪因。

加载 Disqus 评论