在浏览器使用的语言中,我敢打赌,开发者第一个决定需要额外处理的语言是 HTML。世界上每一个 CMS(除了故意只提供无头功能的 CMS)本质上都是一个复杂的 HTML 处理器:它们接受内容,并将其与 HTML 模板一起压缩。如今,存在着数十种其他专门的 HTML 处理语言。
HTML 处理的主要需求是
- 从各个部分组成完整的 HTML 文档
- 通过注入可变数据来模板化 HTML
它们可以拥有许多其他功能,我们稍后会谈到,但我认为这些是主要的功能。
这项研究由 Frontend Masters 提供支持,Frontend Masters 是 CSS-Tricks 的官方学习合作伙伴。
以 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"; |
ERB | ✅<%= title %> |
Markdown | ❌ |
PHP | ✅<?php echo $post.title; ?> 还具有 HEREDOC 语法。 |
Slim | ✅tr |
Haml | ✅<h1><%= post.title %></h1> |
Liquid | ✅Hello {{ user.name }}! |
Go html/template | ✅{{ .Title }} |
Handlebars | ✅{{firstname}} {{lastname}} |
Mustache | ✅Hello {{ firstname }}! |
Twig | ✅{{ foo.bar }} |
Nunjucks | ✅<h1>{{ title }}</h1> |
Kit | ✅<!-- $myVar = We finish each other's sandwiches. --> |
Sergey | ❌ |
它是否支持部分/包含?
你可以从更小的部分组成 HTML 吗?
处理器 | 示例 |
---|---|
Pug | ✅include includes/head.pug |
ERB | ✅<%= render 'includes/head' %> |
Markdown | ❌ |
PHP | ✅<?php include 'head.php'; ?> |
Slim | ⚠️ 如果你可以访问 Ruby 代码,它看起来可以 做到这一点,但你需要编写自定义帮助程序。 |
Haml | ✅.content |
Liquid | ✅{% render head.html %} |
Go html/template | ✅{{ partial "includes/head.html" . }} |
Handlebars | ⚠️ 只能通过 预先注册部分 来实现。 |
Mustache | ✅{{> next_more}} |
Twig | ✅{{ include('page.html', sandboxed = true) }} |
Nunjucks | ✅{% include "missing.html" ignore missing %} |
Kit | ✅<!-- @import "someFile.kit" --> |
Sergey | ✅<sergey-import src="header" /> |
它是否支持包含局部变量?
也就是说,你可以传递数据给包含/部分,让它专门使用吗?例如,在 Liquid 中,你可以传递第二个变量参数供部分使用。但在 PHP 或 Twig 中,没有这种能力——它们只能访问全局变量。
处理器 | 示例 |
---|---|
PHP | ❌ |
ERB | ✅<%= render( |
Markdown | ❌ |
Pug | ⚠️ Pug 有 混合,你可以将它们放在一个单独的文件中并调用。它不是完全相同的概念,但可以类似地使用。 |
Slim | ❌ |
Haml | ✅.content |
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') %} |
Kit | ❌ |
Sergey | ❌ |
它是否支持循环?
有时候你只需要 100 个 <div>
,你知道吗?或者更常见的情况是,你需要遍历一个数据数组,并为每个条目输出 HTML。有很多不同类型的循环,但至少有一个循环很好,而且你通常可以使它适用于你需要循环的任何内容。
处理器 | 示例 |
---|---|
PHP | ✅for ($i = 1; $i <= 10; $i++) { |
ERB | ✅<% for i in 0..9 do %> |
Markdown | ❌ |
Pug | ✅for (var x = 1; x < 16; x++) |
Slim | ✅- for i in (1..15) |
Haml | ✅(1..16).each do |i| |
Liquid | ✅{% for i in (1..5) %} |
Go html/template | ✅{{ range $i, $sequence := (seq 5) }} |
Handlebars | ✅{{#each myArray}} |
Mustache | ✅{{#myArray}} |
Twig | ✅{% for i in 0..10 %} |
Nunjucks | ✅{% set points = [0, 1, 2, 3, 4] %} |
Kit | ❌ |
Sergey | ❌ |
它是否支持逻辑?
Mustache 以其“无逻辑”的理念而闻名。因此,有时候希望模板语言不包含任何其他功能,迫使你处理另一层中的业务逻辑。有时候,模板中只需要一点点逻辑。实际上,即使是 Mustache 也有一些基本的逻辑。
处理器 | 示例 |
---|---|
Pug | ✅#user |
ERB | ✅<% if show %> |
Markdown | ❌ |
PHP | ✅<?php if (value > 10) { ?> |
Slim | ✅- unless items.empty? 如果您开启了 逻辑无关模式- article |
Haml | ✅if data == true |
Liquid | ✅{% if user %} |
Go html/template | ✅{{ if isset .Params "title" }} |
Handlebars | ✅{{#if author}} |
Mustache | ✅ 有点讽刺的是,Mustache 自称为“逻辑无关模板”,但它们确实有点逻辑,以 “反转部分” 的形式。 {{#repo}} |
Twig | ✅{% if online == false %} |
Nunjucks | ✅{% if hungry %} |
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"; |
Slim | ⚠️ 仅限私有? |
Haml | ⚠️ 一个非常特殊的用于 删除空白 的过滤器。主要用于 嵌入其他语言? |
Liquid | ✅ 有很多过滤器,您可以使用多个过滤器。 {{ "adam!" | capitalize | prepend: "Hello " }} |
Go html/template | ⚠️ 有很多 函数,其中许多是类似过滤器的函数。 |
Handlebars | ⚠️ 三重括号执行 HTML 转义,否则,您需要注册自己的 区块助手。 |
Mustache | ❌ |
Twig | ✅{% autoescape "html" %} |
Nunjucks | ✅{% filter replace("force", "forth") %} |
Kit | ❌ |
Sergey | ❌ |
它有数学运算吗?
有时,数学运算直接嵌入到语言中。一些语言建立在其他语言的基础之上,因此使用其他语言进行数学运算。例如,Pug 是用 JavaScript 编写的,因此您可以在 Pug 中编写 JavaScript,JavaScript 可以进行数学运算。
处理器 | 支持 |
---|---|
PHP | ✅<?php echo 1 + 1; ?> |
ERB | ✅<%= 1 + 1 %> |
Markdown | ❌ |
Pug | ✅- const x = 1 + 1 |
Slim | ✅- x = 1 + 1 |
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 %} |
Nunjucks | ✅{% block item %} |
Kit | ❌ |
Sergey | ✅<sergey-slot /> |
它有特殊的 HTML 语法吗?
HTML 有 <尖括号> 和 <方括号>,虽然空白字符很重要(空格就是空格,但 80 个空格也是……一个空格),但它并非像 Pug 或 Python 那样真正依赖空白字符的语言。更改这些内容是语言的选择。如果该语言只添加了额外的语法,但您像通常一样编写 HTML,我认为这不是特殊语法。如果该语言更改了您编写普通 HTML 的方式,那就是特殊语法。
处理器 | 示例 |
---|---|
PHP | ❌ |
ERB | 在 Ruby 中,如果您想要使用特殊的 HTML 语法,您通常使用 Haml。 |
Markdown | ✅ 这几乎是 Markdown 的核心功能。 # 标题 |
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 | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ⚠️ |
Pug 可以通过混合使用局部变量。
这是文档:https://pug.node.org.cn/language/mixins.html
我不确定混合是否可以放在单独的文件中,但可以吗?
是的,混合可以使用任意 .pug 文件,您只需要将它包含在本地 .pug 文件中。我喜欢将所有混合都放在一个 mixins.pug 文件中,这样您只需要包含一个包含所有混合的文件。
我将我的混合加载到布局文件的顶部,这样任何使用该布局的页面都可以访问这些混合。
但是您可以将本地变量传递给 Twig 包含文件。这就是
with
关键字的作用不错,已更新。
是的,或者使用函数语法
Twig 在包含文件中使用本地变量。
它在包含文件中使用本地变量吗?
Slim 实现了 Haml/Erb 的所有功能。它可以调用任何 Ruby 方法,包括 render。
= render ...
Slim 另一个有趣的地方是您可以在单个文件中编写完整的 CSS/JavaScript/Ruby 代码块。这与 Vue 的 SFC 类似。
我基本上将此功能用作简易的 Rails 组件。结合辅助方法,您甚至可以相对容易地单独测试它。
您忘了 ASP.NET Core 的 Razor 模板,它已经存在了 12 年
https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-5.0
它是跨平台的、开源的,并且拥有上面提到的所有功能,甚至更多
Chris,这是一篇很棒的文章。感谢您的努力。但为什么您称之为预处理器,以及为什么是 HTML?回到 2000 年,我做了一个名为模板引擎的预处理器 (https://github.com/drogatkin/aldan3/blob/master/src/java/org/aldan3/util/TemplateEngine.java),它是多功能的,您可以生成任何内容,包括 HTML。我仍然在使用它。
Handlebars 可以实现插槽/块,但语法并不十分直观
希望您可以比较每个的 SSR 构建能力。
这是许多人最感兴趣的部分。☺️
这些都支持 SSR。