比较 HTML 预处理器功能

在浏览器使用的语言中,我敢打赌,开发者第一个决定需要额外处理的语言是 HTML。世界上每一个 CMS(除了故意只提供无头功能的 CMS)本质上都是一个复杂的 HTML 处理器:它们接受内容,并将其与 HTML 模板一起压缩。如今,存在着数十种其他专门的 HTML 处理语言。

HTML 处理的主要需求是

  • 从各个部分组成完整的 HTML 文档
  • 通过注入可变数据来模板化 HTML

它们可以拥有许多其他功能,我们稍后会谈到,但我认为这些是主要的功能。

这项研究由 Frontend Masters 提供支持,Frontend Masters 是 CSS-Tricks 的官方学习合作伙伴。

需要前端开发培训吗?

Frontend Masters 是学习的最佳场所。他们提供涵盖所有最重要的前端技术的课程,从 ReactCSS,从 VueD3,以及 Node.js全栈 等更高级的课程。

Diagram showing partials and {{ data }} turning into a complete HTML document.

以 PHP 为例。它实际上就是“超文本预处理器”。在这个网站上,我使用 PHP 将模板化的 HTML 片段拼凑在一起,从而构建了你现在看到的页面和完整内容。

<h2>
  <?php the_title(); // Templating! ?>
</h2>

<?php include("metadata.php"); // Partials! ?>

在上面的代码中,我将一些内容压缩到一个 HTML 模板中,该模板调用另一个可能包含更多模板化 HTML 的 PHP 文件。PHP 涵盖了 HTML 处理的两个主要需求,并且可以通过经济实惠的托管服务获得——我认为这是 PHP 网站占互联网很大一部分 的一个重要原因。

但 PHP 绝不是唯一的 HTML 预处理器,而且它需要服务器才能运行。还有很多其他的预处理器,其中一些专门设计在网站被用户请求之前构建过程中运行。

让我们逐一语言看看它是否支持某些功能以及如何支持。如果可能的话,预处理器名称的链接会链接到相关的文档。

它允许模板化吗?

你可以在最终的 HTML 输出中混合数据吗?

处理器示例
Pug
- var title = "On Dogs: Man's Best Friend";
- var author = "enlore";
h1= title
p Written with love by #{author}
ERB
<%= title %>
<%= description %>

<%= @logged_in_user.name %>
Markdown
PHP
<?php echo $post.title; ?>
<?php echo get_post_description($post.id) ?>
还具有 HEREDOC 语法。
Slim
tr
td.name = item.name
Haml
<h1><%= post.title %></h1>
<div class="subhead"><%= post.subtitle %></div>
Liquid
Hello {{ user.name }}!
Go html/template
{{ .Title }}
{{ $address }}
Handlebars
{{firstname}} {{lastname}}
Mustache
Hello {{ firstname }}!
Twig
{{ foo.bar }}
Nunjucks
<h1>{{ title }}</h1>
Kit
<!-- $myVar = We finish each other's sandwiches. -->
<p> <!-- $myVar --> </p>
Sergey

它是否支持部分/包含?

你可以从更小的部分组成 HTML 吗?

处理器示例
Pug
include includes/head.pug
ERB
<%= render 'includes/head' %>
Markdown
PHP
<?php include 'head.php'; ?>
<?php include_once 'meta.php'; ?>
Slim⚠️
如果你可以访问 Ruby 代码,它看起来可以 做到这一点,但你需要编写自定义帮助程序。
Haml
.content
=render 'meeting_info'
Liquid{% render head.html %}
{% render meta.liquid %}
Go html/template{{ partial "includes/head.html" . }}
Handlebars⚠️
只能通过 预先注册部分 来实现。
Mustache{{> next_more}}
Twig{{ include('page.html', sandboxed = true) }}
Nunjucks{% include "missing.html" ignore missing %}
{% import "forms.html" as forms %}
{{ forms.label('Username') }}
Kit<!-- @import "someFile.kit" -->
<!-- @import "file.html" -->
Sergey<sergey-import src="header" />

它是否支持包含局部变量?

也就是说,你可以传递数据给包含/部分,让它专门使用吗?例如,在 Liquid 中,你可以传递第二个变量参数供部分使用。但在 PHP 或 Twig 中,没有这种能力——它们只能访问全局变量。

处理器示例
PHP
ERB<%= render(
partial: "card",
locals: {
title: "Title"
}
) %>
Markdown
Pug⚠️
Pug 有 混合,你可以将它们放在一个单独的文件中并调用。它不是完全相同的概念,但可以类似地使用。
Slim
Haml.content
= render :partial => 'meeting_info', :locals => { :info => @info }
Liquid{% render "name", my_variable: my_variable, my_other_variable: "oranges" %}
Go html/template{{ partial "header/site-header.html" . }}
(末尾的句点表示 “变量作用域”。)
Handlebars{{> myPartial parameter=favoriteNumber }}
Mustache
Twig
{% include 'template.html' with {'foo': 'bar'} only %}
Nunjucks{% macro field(name, value='', type='text') %}
<div class="field">
<input type="{{ type }}" name="{{ name }}" value="{{ value | escape }}" />
</div>
{% endmacro %}
Kit
Sergey

它是否支持循环?

有时候你只需要 100 个 <div>,你知道吗?或者更常见的情况是,你需要遍历一个数据数组,并为每个条目输出 HTML。有很多不同类型的循环,但至少有一个循环很好,而且你通常可以使它适用于你需要循环的任何内容。

处理器示例
PHPfor ($i = 1; $i <= 10; $i++) {
echo $i;
}
ERB<% for i in 0..9 do %>
<%= @users[i].name %>
<% end %>
Markdown
Pugfor (var x = 1; x < 16; x++)
div= x
Slim- for i in (1..15)
div #{i}
Haml(1..16).each do |i|
%div #{i}
Liquid{% for i in (1..5) %}
{% endfor %}
Go html/template{{ range $i, $sequence := (seq 5) }}
{{ $i }}: {{ $sequence }
{{ end }}
Handlebars{{#each myArray}}
<div class="row"></div>
{{/each}}
Mustache{{#myArray}}
{{name}}
{{/myArray}}
Twig{% for i in 0..10 %}
{{ i }}
{% endfor %}
Nunjucks{% set points = [0, 1, 2, 3, 4] %}
{% for x in points %}
Point: {{ x }}
{% endfor %}
Kit
Sergey

它是否支持逻辑?

Mustache 以其“无逻辑”的理念而闻名。因此,有时候希望模板语言不包含任何其他功能,迫使你处理另一层中的业务逻辑。有时候,模板中只需要一点点逻辑。实际上,即使是 Mustache 也有一些基本的逻辑。

处理器示例
Pug#user
if user.description
h2.green Description
else if authorised
h2.blue Description
ERB<% if show %>
<% endif %>
Markdown
PHP<?php if (value > 10) { ?>
<?php } ?>
Slim- unless items.empty?如果您开启了 逻辑无关模式
- article
h1 = title
-! article
p 抱歉,文章未找到
Hamlif data == true
%p true
else
%p false
Liquid{% if user %}
您好 {{ user.name }}!
{% endif %}
Go html/template{{ if isset .Params "title" }}
<h4>{{ index .Params "title" }}</h4>
{{ end }}
Handlebars{{#if author}}
{{firstName}} {{lastName}}
{{/if}}
Mustache
有点讽刺的是,Mustache 自称为“逻辑无关模板”,但它们确实有点逻辑,以 “反转部分” 的形式。
{{#repo}}
{{name}}
{{/repo}}
{{^repo}}
没有仓库 :(
{{/repo}}
Twig{% if online == false %}
我们的网站处于维护模式。
{% endif %}
Nunjucks{% if hungry %}
我饿了
{% elif tired %}
我累了
{% else %}
我很好!
{% endif %}
Kit
如果变量存在,它可以输出变量,它称之为“可选”
<dd class='<!-- $myVar? -->'> 第 1 页 </dd>
Sergey

它有过滤器吗?

我在这里所说的过滤器是指一种输出内容但更改输出内容的方式。例如,转义特殊字符或将文本大写。

处理器示例
Pug⚠️
Pug 将 过滤器 视为在 Pug 中使用其他语言的方式,并且它没有自带任何过滤器。
ERB
无论 Ruby 有什么,比如
"hello James!".upcase #=> "HELLO JAMES!"
Markdown
PHP$str = "Mary Had A Little Lamb";
$str = strtoupper($str);
echo $str; // Prints MARY HAD A LITTLE LAMB
Slim⚠️
仅限私有?
Haml⚠️
一个非常特殊的用于 删除空白 的过滤器。主要用于 嵌入其他语言
Liquid
有很多过滤器,您可以使用多个过滤器。
{{ "adam!" | capitalize | prepend: "Hello " }}
Go html/template⚠️
有很多 函数,其中许多是类似过滤器的函数。
Handlebars⚠️
三重括号执行 HTML 转义,否则,您需要注册自己的 区块助手
Mustache
Twig{% autoescape "html" %}
{{ var }}
{{ var|raw }} {# var 不会被转义 #}
{{ var|escape }} {# var 不会被双重转义 #}
{% endautoescape %}
Nunjucks{% filter replace("force", "forth") %}
may the force be with you
{% endfilter %}
Kit
Sergey

它有数学运算吗?

有时,数学运算直接嵌入到语言中。一些语言建立在其他语言的基础之上,因此使用其他语言进行数学运算。例如,Pug 是用 JavaScript 编写的,因此您可以在 Pug 中编写 JavaScript,JavaScript 可以进行数学运算。

处理器支持
PHP<?php echo 1 + 1; ?>
ERB<%= 1 + 1 %>
Markdown
Pug- const x = 1 + 1
p= x
Slim- x = 1 + 1
p= x
Haml%p= 1 + 1
Liquid{{ 1 | plus: 1 }}
Go html/template
{{add 1 2}}
Handlebars
Mustache
Twig{{ 1 + 1 }}
Nunjucks{{ 1 + 1 }}
Kit
Sergey

它有插槽/区块吗?

插槽的概念是一个模板,它在模板中包含特殊区域,如果内容可用,这些区域将填充内容。它在概念上类似于局部,但几乎是反向的。就像您可以将包含局部的模板视为模板调用这些局部来组成一个页面,您几乎可以将插槽视为数据调用模板将其转换为一个完整的页面。Vue 以 拥有插槽 而闻名,这是一个 已融入 Web 组件 的概念。

处理器示例
PHP
ERB
Markdown
Pug
您可以使用 “混合” 实现它
Slim
Haml
Liquid
Go html/template
Handlebars
Mustache
Twig{% block footer %}
© 版权所有 2011 年,由您所有。
{% endblock %}
Nunjucks{% block item %}
项目名称为:{{ item.name }}
{% endblock %}
Kit
Sergey<sergey-slot />

它有特殊的 HTML 语法吗?

HTML 有 <尖括号> 和 <方括号>,虽然空白字符很重要(空格就是空格,但 80 个空格也是……一个空格),但它并非像 Pug 或 Python 那样真正依赖空白字符的语言。更改这些内容是语言的选择。如果该语言只添加了额外的语法,但您像通常一样编写 HTML,我认为这不是特殊语法。如果该语言更改了您编写普通 HTML 的方式,那就是特殊语法。

处理器示例
PHP
ERB在 Ruby 中,如果您想要使用特殊的 HTML 语法,您通常使用 Haml。
Markdown
这几乎是 Markdown 的核心功能。
# 标题
包含 [链接](#link) 的段落。

- 列表
- 列表

> 引述
Pug
Slim
Haml
Liquid
Go html/template
Handlebars
Mustache
Twig
Nunjucks
Kit⚠️
HTML 注释指令。
Sergey⚠️
一些自创的 HTML 标签。

等等,React 和 Vue 之类的工具怎么办?

我同意这些技术是基于组件的,用于执行模板化,并经常制作完整的页面。它们还可以执行此处列出的许多/大多数功能。它们以及许多其他基于 JavaScript 的框架也通常能够在服务器上或在构建步骤期间运行并生成 HTML,即使它有时感觉像是事后想法(但 并非总是如此)。它们还具有一些其他功能,这些功能可能非常引人注目,例如作用域/封装的样式,这需要 HTML 和 CSS 之间的协作,这是一个诱人的功能。

我没有将它们包含在内,因为它们通常被有意地用于本质上制作 DOM。它们专注于数据检索和操作、状态管理、交互性等方面。它们并不真正专注于仅仅作为 HTML 处理器。如果您使用的是 JavaScript 框架,可能不需要专门的 HTML 处理器,虽然绝对可以做到。例如,混合 Markdown 和 JSX 或混合 Vue 模板和 Pug

我甚至没有将原生 Web 组件列入此列表,因为它们非常以 JavaScript 为中心。

其他注意事项

  • 速度处理速度如何?您关心吗?
  • 语言 — 它是用什么语言编写的?它与您需要支持的机器兼容吗?
  • 服务器或构建 — 它是否需要运行 Web 服务器才能工作?还是可以在构建过程中运行一次?或者两者都有?

超级图表

模板包含局部变量循环逻辑过滤器数学插槽特殊语法
PHP
ERB⚠️
Markdown
Pug⚠️
Slim⚠️⚠️
Haml⚠️
Liquid
Go html/template⚠️
Handlebars⚠️
Mustache
Twig
Nunjucks
Kit⚠️
Sergey⚠️