我在职业生涯中很大一部分时间都在使用 WordPress,并从中了解到两件事:流行会导致轻视,仅仅因为其他人的批评而否定某些东西,错失了机会。
WordPress 和我一开始相处得很好,但随着时间的推移,我们变得越来越疏远。并不是我们在一起的时间越来越少,而是感觉我们只是在按部就班地进行。激情正在消退,但我仍然知道这是我工作的重要组成部分。有时需要改变,而这正是 Timber 重新将我们联系在一起的契机。
我知道 WordPress 并非对所有人或所有事物来说都是完美的工具,但还有什么工具是完美的呢?它有自己的时间和空间,我希望能够为你指明一些你可能错过的道路。
什么是 Twig?

Twig 是一种很棒的 PHP 模板语言。不要被名字迷惑;它一点也不脆弱。
如果你曾经见过任何 JavaScript 模板语言,比如 Handlebars 或 Moustache,那么它看起来会很熟悉。如果没有,也不要担心;Twig 非常棒,它提供了简洁、易于理解的语法来使用。看看他们主页上的这个示例
<?php echo $var ?>
<?php echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8') ?>
{{ var }}
{{ var|escape }}
{{ var|e }} {# shortcut to escape a variable #}
告诉我这没有激发你的兴奋!
要输出一个变量,你只需用双花括号括起来,省略美元符号,例如:{{ var }}
。你也可以访问变量的属性,例如:{{ user.name }}
控制结构出现在 {% ... %}
块中,使用起来也很友好
{% for post in posts %}
{{ post.title }}
{% endfor %}
你可以从他们的 文档 中了解更多信息。
想象一下,像这样编写你的 WordPress 模板…
什么是 Timber?

Timber 是将 WordPress 与 Twig 结合在一起的插件。除了使用 Twig 的好处之外,它还允许你将 PHP 与 HTML 模板分离。这反过来又为开发主题提供了更轻松的环境,我们稍后会探讨这一点。
让我们看看文档中的一个示例,然后逐步解析它
$context = Timber::get_context();
$context['post'] = new TimberPost(); // It's a new TimberPost object, but an existing post from WordPress.
Timber::render('single.twig', $context);
`single.twig` 文件
<article>
<h1 class="headline">{{post.post_title}}</h1>
<div class="body">
{{post.content}}
</div>
</article>
从 `single.php` 开始,我们首先要做的是使用 Timber::get_context();
获取主题的上下文。这个对象将包含诸如菜单、wp_head
和 wp_footer
之类的内容。稍后我们将看看如何添加其他内容,如果你需要在主题中全局访问其他内容。你将经常使用这行代码。
接下来,我们需要获取我们要在模板中显示的帖子。使用 new TimberPost();
将找出要获取哪个帖子。
最后,我们想要显示一些内容。Timber 的 render
函数将调用我们的 Twig 文件并传递我们刚刚收集的数据。
最后一点爆炸性消息,我希望不要太突然:Timber 内置支持 Advanced Custom Fields。
为什么要使用 Twig?
使用 WordPress 一段时间后,你可能会发现自己难以在 PHP 和 HTML 之间混乱的舞蹈中辨别方向。这并不是说所有主题都是这样,但你可以看到这种情况很容易发生。
我发现 Timber 很棒的一点是,它通过在两者之间创造空间来缓解了这个问题。你永远不会将 PHP 与 HTML 混在一起,但它们仍然可以相互通信。你可能会发现对话更有意义,因为这使 WordPress 的最佳部分有机会闪耀。
例如,我们的 `single.php` 文件只有 3 行,而这仅仅是用来处理数据。当数据到达我们的视图时,我们很高兴知道我们拥有所需的一切 - 剩下的就是标记它。
入门
安装
所以让我们开始吧!如果你愿意,可以使用 composer
安装它,但为了简洁起见,我将通过 **插件 > 添加新插件** 安装它,并搜索 Timber 和 Twig 的关键字。或者,你可以从 插件目录 中获取它,并上传到你的网站。
入门主题
Timber 带有一个基础入门主题,非常适合快速上手。安装插件后,你会发现以下文件夹,可以将其复制到你的主题文件夹中。它应该位于 `wp-content/plugins/timber/timber-starter-theme` 中。或者,如果你只是想看一看,可以 浏览入门主题存储库。
你会注意到,乍一看这里没有什么不寻常的地方。魔法在哪里?在这一点上感到担忧是正常的,所以让我们深入研究一下。
functions.php
`functions.php` 文件的用途在很大程度上保持不变。你可以像往常一样使用它,无论是声明自定义帖子类型,还是定义自定义函数。但是,使用 Timber Starter Theme,你会注意到其中有一个类声明在等待。这个类最值得注意的方法是 add_to_context
。
从这里我们可以添加我们希望在整个主题中访问的项目。例如,如果我在管理面板中创建了一个名为 primary-menu
的菜单,我可以将其添加到全局上下文中,如下所示
$context['primary_menu'] = new TimberMenu('primary-menu');
现在我可以像这样访问它
<nav role="navigation">
<ul>
{% for item in primary_menu.get_items %}
<li class="{{item.classes | join(' ')}}">
<a href="{{item.get_link}}">{{item.title}}</a>
</li>
{% endfor %}
</ul>
</nav>
还不错吧!另一个我想分享的例子是使用 多环境配置 之类的东西。这将创建一个常量来表示当前环境,例如开发、预发布或生产。我可以通过将以下内容添加到 add_to_context
方法中,轻松地将这些信息提取到我的主题中
$context['env'] = WP_ENV;
在我的模板中,我可以调用 {{ env }}
来告诉我我的网站正在运行哪个环境。
扩展和块
让我们看看 `index.php`。
$context = Timber::get_context();
$context['posts'] = Timber::get_posts();
$templates = array( 'index.twig' );
if ( is_home() ) {
array_unshift( $templates, 'home.twig' );
}
Timber::render( $templates, $context );
好的,我们之前已经看到过大部分内容。这里有一些额外的内容,比如有一个模板数组。所有这些操作只是在 is_home()
返回 true 时将 `home.twig` 添加到数组的开头。
假设 is_home()
返回 false,那么它将告诉它渲染 `index.twig`。Twig 文件位于 `views` 目录中,所以我们将从那里开始,在我们的 `index.twig` 文件中
{% extends "base.twig" %}
{% block content %}
{% for post in posts %}
{% include 'tease.twig' %}
{% endfor %}
{% endblock %}
这里要介绍几个新内容,分别是 extends
和 block
。如果你不确定它们的功能,我相信你很快就会明白的。
当我们调用 extends
时,我们表示“对我在这个模板中声明的任何内容使用此布局”。然后在 `base.twig` 中,你会看到这样的内容
<!doctype html>
<html>
<head>
...
</head>
<body>
...
{% block content %}
No content found!
{% endblock %}
</body>
</html>
通常在 WordPress 中,我们可能只是在每个模板中包含页脚和页眉,但在这里它们都在同一个文件中,我们告诉我们的模板进行扩展。
你可以随意命名你的块。那串“未找到内容!” 只有在 content
块中没有提供任何内容时才会显示。在 Timber Starter Theme 中,你会看到他们为页眉和页脚的部分声明了额外的块,但重要的是使用对你来说有意义的东西。
自定义模板
说够了,现在该自己动手做模板了!本教程将使用Advanced Custom Fields,以及 Timber + Twig 的一些额外功能,肯定会让你感到满意。
我们将创建一个非常简单的示例页面,该页面包含一个横幅图像,一些文字横跨其上。如果需要,这是一个英雄元素。我们的英雄将向我们展示 Twig 的表现力,并挑战我们对 WordPress 的认知模型。我们假设我们的英雄元素已使用 Advanced Custom Fields 定义。您可以使用以下方法调用自定义字段:post.get_field('field_name');
。如果您使用的是 WordPress 自己的自定义字段,则可以像这样使用:post.field_name
。
有两种方法可以创建自定义页面模板,由于在我的情况下,我并不总是知道最终的 URL,所以我将采用“自定义 PHP 文件”的方法。最后,让我们开始输入内容吧!
base.twig
对于我们的基本布局,我将 Timber 的入门主题布局文件与 HTML5 Boilerplate 的 index 文件结合起来
<!doctype html>
<html class="no-js" {{site.language_attributes}}>
<head>
<meta charset="{{site.charset}}">
<title>
{% if wp_title %}
{{ wp_title }} - {{ site.name }}
{% else %}
{{ site.name }}
{% endif %}
</title>
<meta name="description" content="{{site.description}}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{{ site.theme.link }}/style.css">
</head>
<body>
<nav role="navigation">
<ul>
{% for item in primary_menu.get_items %}
<li class="nav-item {{item.classes | join(' ')}}">
<a class="nav-link" href="{{item.get_link}}">{{item.title}}</a>
</li>
{% endfor %}
</ul>
</nav>
<div class="wrapper">
{% block content %}
No content found!
{% endblock %}
</div>
</body>
</html>
调用{{ site.theme.link }}
会非常有用,如果您使用的是 Wiredep + Gulp,您可以查看此代码段,了解如何将其融入您的工作流程。
自定义模板文件
如我们已经看到,您的 PHP 文件将非常简单。在your-custom-page.php
中几行代码就足够了
<?php
/*
* Template Name: Your Custom Page
*/
$context = Timber::get_context();
$post = new TimberPost();
$context['post'] = $post;
Timber::render( 'your-custom-page.twig', $context );
这就是我们所需要的!我们的英雄your-custom-page.twig
文件在等着我们…
{% extends 'base.twig' %}
{% block content %}
<section>
<h1>{{ post.get_field('hero_title') }}</h1>
<img src="{{ TimberImage(post.get_field('hero_image')).src }}" alt="{{ TimberImage(post.get_field('hero_image')).alt }}">
</section>
{% endblock %}
我们在这里使用TimberImage
来获取有关图像所需的信息。在这种情况下,它是 URL 和 alt 文本。
我们可以将其保留在那里… 但既然我们已经开始行动了,我们将利用resize
过滤器。它需要 3 个参数:宽度、高度和裁剪。我们只指定宽度。
让我们重构我们的英雄图像,使其更具响应性
<img alt="{{ TimberImage(post.get_field('hero_image')).alt }}"
srcset="
{{ TimberImage(post.get_field('hero_image')).src | resize(1600)}} 1600w,
{{ TimberImage(post.get_field('hero_image')).src | resize(1000)}} 1000w,
{{ TimberImage(post.get_field('hero_image')).src | resize(700)}} 700w
"
src="{{ TimberImage(post.get_field('hero_image')).src | resize(1000)}}"
sizes="100vw"
>
是的,您可以做到。
结论
WordPress 有很多东西可以提供,我承认自己一直把它当作理所当然。热爱你的工作本身就是一种回报,我认为花点时间用新的方式来处理某件事,是一个培养新技能和重新激发动力的绝佳机会。我无法告诉你我用 WordPress 构建了多少网站,但我可以告诉你我第一次使用 Timber 时,我的思想发生了怎样的改变。
这是一篇很棒的文章,感谢 TJ 的概述!
很高兴你喜欢!谢谢 :)
我喜欢使用 Timber,并且在过去的一年中一直在使用它。作为一名前端开发人员,我的 PHP 编码经验并不丰富,Timber 使一切都变得清晰易懂。
我从未使用过 Timber,但我喜欢响应式图像示例。它似乎真正绕过了 WordPress 令人沮丧的预设缩略图大小逻辑。
当我开始使用 Timber 时,让我担心的一件事是,我是否必须重新构建所有标准的 WordPress 函数调用,比如 the_title()。不用担心,Timber 已经为您做好了准备。只需将任何 WP 函数调用包装在 {{function()}} 标签中即可。例如,{{function(‘wp_title’, ‘any parameter’)}}。很简单。
我尝试过 Timber,但不知道这个,而且不知何故在文档中错过了它,所以我停止使用它。
是时候再试一次了!
实际上,TJ,如果你考虑写一篇后续文章,我斗胆请求你讨论一下如何集成动态侧边栏/小部件。Timber 网站上的文档似乎非常混乱,通常我需要去 Stack Overflow 一两趟才能让小部件正常工作。
嗨,尼克,
我可以给你举一个我目前正在做的项目的例子。我将概述几个步骤
在
functions.php
中,您将像这样初始化小部件之后,您可以通过仪表盘添加您想要的任何内容。然后,在您的 PHP 文件中,您可以通过调用您为其分配的名称来将其添加到上下文/数据中
最后,在您的 .twig 文件中,您可以像这样访问它
希望这能有所帮助!
我认为,输出应该默认进行转义。
对于我的大多数项目,我已经从 WordPress 切换到https://Bolt.cm。
Bolt 原生使用 Twig,比 WordPress 更灵活、更易用。如果您欣赏 Timber 和 Twig 的优势,那么您很可能会喜欢在下一个项目中完全切换到 Bolt。
不过,有时 WP 是不可避免的,Timber 是这些情况下的一个好解决方案。感谢这篇文章,我在这里学到了至少 2 个新技巧,并且将收藏起来。
Bolt 看起来很不错!不过,我想要表达的是,WP 不必是我们忍耐的东西。它实际上很有表现力,有时我们可能会因为流行观点或糟糕的体验而忽略了这一点,这些体验影响了我们的印象。
然而,和所有其他事物一样,它并不适合所有人,也不必适合所有人。我将尝试使用 Bolt,并且我期待着它。
看起来很有趣!您如何处理通常通过使用插件来解决的项目,例如用于简单电子商务集成的 Woocommerce,或用于简单到高级表单集成的 Gravity Forms?
WordPress 的核心优势之一是插件社区和易于扩展性。Bolt 如何处理这些问题或进行比较?
@拉尔斯·费耶
扩展/插件和主题可在http://extensions.bolt.cm * 上获取,也可以通过 Bolt 后端的几次点击安装。对于大多数扩展,功能尽可能与布局分离。
默认情况下,自定义字段或内容类型、路由或扩展的用户/组管理等功能都是可用的,因此您可能需要的插件比您预期的要少。对于类似 Gravityforms 的功能,Boltforms 扩展是一个不错的选择。
Bolt 的目标不是建立网上商店,但我看到人们将 Bolt 与 Shopify 等服务结合使用。
*)在我写这篇文章时,这个网站似乎正在进行维护,这可能是您错过的原因。
您应该查看 Laravel 的 Blade 语法。同样实用和优雅。
https://laravel.net.cn/docs/5.1/blade
我非常喜欢 Laravel,每次使用 Blade 都让我感到高兴。
@埃弗特·阿尔伯斯
是的,当您在 Google 上搜索“WordPress 替代品”时,您的名字会出现在所有所谓的“有用”网站上,它们都在讲述着同样的故事。
回到正题,
我对这个 Twig 东西不太了解... 它真的没有给我那种在 HTML 标签之间使用 PHP 函数标签的舒适感。几年前当我开始创建静态 HTML 网站时,我确实遇到了一个巨大的 CMS 问题。我只掌握了 HTML 和 CSS 的一些知识,这些知识很容易学习,对于初学者来说,效果很好/令人满意。创建 CMS 确实是一件大胆的技术事。一切都进展顺利,问题是更新所有这些静态网站,人们每次需要更改他们的网站时都必须问我,所以我开始研究 WordPress,因为全世界的人都说,这就是它,对于初学者来说,而且它确实... 一些人喜欢贬低 WordPress,说它臃肿且过度使用,但说实话,在过去的几年里我从未有过这种感觉,而且今天没有像 WordPress 这样的东西。
我已经搜索了几个月,想切换并放弃 WordPress,但出于某种原因,这根本不可行,我总是回到它那里,吃掉核心开发者每次新版本发布时都会带来的所有花哨的东西。他们仍然创建了一些非常可靠的代码,但 WordPress 中现在有太多实际上不需要的垃圾。
我的意思是 WordPress 功能如此强大,即使没有太多 PHP 知识,只有可靠的 HTML/CSS 知识,你也可以从头开始创建自己的主题,PHP 函数标签可读且易于理解它们的作用。 或告诉你确切的期望,它不可能比这更容易了... 只需将这些 PHP 代码/函数包裹在 HTML 标签之间就可以了。如果你了解 WordPress 模板层次结构,你就已经完成了一半。
当我开始在 Twenty Ten(主题)区域创建 WP 主题时,这就是它有多么可爱。如今,WordPress 模板结构变得非常无聊,就像,模板中的 HTML 越来越少,而这些越来越多,<?php the_title('’, ”) ?>。如果已经发展到这种程度,老天爷,我只能说你在你的“易于使用”方面已经走到了尽头。这只是一个非常简单的例子,在 WordPress 中还有更多丑陋的东西需要展示,但我对这个平台太尊重了,甚至说这些话都感到难过。
我最讨厌的是整个 Customizer.js 垃圾。说真的,如果你需要一个半成品的前端,一个小型的侧边栏工具栏来更改你的网站名称或颜色,我认为你遇到了大麻烦。这可能在 wordpress.COM 上运作得很好,但在自托管情况下呢??我不这么认为。我明白控制面板最终会消失,我们将在前端直接发布 + 编辑,但 WordPress Customizer 并不是 CMS 环境中前进的方向。但是,嘿,如果有些人喜欢它,那就继续吧,但不要用它来窒息我。如果我不喜欢在主题中使用 Customizer,我现在不能只是用 functions.php 中的单个过滤器从核心功能中删除它。不,我必须安装一个插件,或者在管理区域编写 35 行代码,包括自定义 CSS 来隐藏与 Customizer 有关的所有内容。
WordPress 今天变得越来越不漂亮了。我相信核心开发者正在创建他们个人非常喜欢的太多花哨的东西,而不是创建社区真正需要的东西。
我真的很希望那里有一些“新的”和非常基本的东西,它真正专注于前端开发,但仍然具有一些控制面板、后端集成,以便人们可以对他们的网站进行一些更改。也许有一些具有可靠的帖子类型基础的东西,因为 WordPress 中的自定义帖子类型集成方式简直太美了。我只是不需要它周围的所有花哨东西。
该死,在过去的几年里我非常喜欢 WordPress,但它真的已经发展到了不再有趣和漂亮的地步。
@shmoo
我发现你指责 Evert Albers 向可能不知道 Bolt 的人推荐它,然后又列出了一些你不喜欢 WordPress 当前状态的内容,这有点讽刺。
有趣的是,你提到的每一件事都是我们在 Bolt 中做得不同且更好的事情。
除非你做一些非常不规范的事情,否则 Twig(以及使用 Twig 的 CMS)不允许你在模板中使用 PHP 函数。虽然这可能看起来像你失去了自由,但它实际上促进了良好的实践:将业务逻辑(PHP 东西,应该由 PHP/控制器/CMS 处理)和展示(HTML)分离。
大多数 Bolt 配置都是通过 YML 文件完成的。这可能看起来很麻烦,但它非常灵活且易于使用。没有笨重的复选框或处理数百万个输入。只需在浏览器或你喜欢的编辑器中修改
.yml
即可。作为额外奖励,你只需将这些文件保存在你的版本控制系统中,以便更轻松地进行开发和部署。自从我们开始以来,我们就非常明确地表明我们不会朝那个方向发展。基本上,每个 CMS 都有三种类型的用户:编辑、前端开发人员和后端开发人员。他们每个人都同等重要。我们甚至把它写进了我们的宣言中。
这基本上就是 Bolt 的功能。
在 Bolt 中,它们被称为内容类型,并且可以非常快速、简单地修改为适应你的网站需求。(参见上面的关于 .yml 的要点)
说真的,你应该查看 Bolt 并加入我们的 IRC 频道。我们是一群友好的人,总是乐意帮助新手。
很棒的文章,感谢 TJ!我只有一个问题,你的文章以及官方 Timber 文档都没有处理过。如何处理像 is_front_page、is_page()(例如 is_page(7)、is_404 等等)这样的条件函数。WordPress 手册中有相当一部分。
在 Timber 文档中,它们没有提到。如何最好地处理这些问题?将 if 逻辑移动到 PHP 文件中,而不是在 Twig 中使用 if else 功能?或者留在 Twig 中,并使用特殊的语法?
我还没有尝试过,但它对其他东西有效:你可以将函数包装在一个 Timber {{ function }} 标签中,并在 Twig 中使用条件来设置一个变量?例如
然后你可以在模板的其余部分重复使用它,在其他条件中。
你也可以将 PHP 函数重新实现为 Twig 函数,但这超出了我的舒适范围。
TJ,感谢你清楚地说明了这个例子,有一个简洁的参考真的很棒。
Twig 很棒!为什么不全力以赴,直接使用 Craft 呢?这样你就可以避免 WordPress 带来的所有其他麻烦。;
+1 Craft 用起来好多了。
“直接使用 _______”
说真的,+2 支持 Craft。
WP 在某些情况下很棒,但如果你要使用 ACF 来处理字段,再加上 Timber 来获得 Twig,那么你就有两个额外的依赖项需要在每次构建中依赖。
我通常最终会禁用评论,并添加一些自定义帖子类型,这可能需要你根据你正在构建的网站类型,再添加 1 或 2 个插件。所以现在你至少需要 4 个插件。你可能还希望在未来的某个时候使用某种缓存解决方案。
Craft 将所有这些功能内置,并结合了一个更好的界面。在客户/设计师环境中,它运作得很好,但显然 YMMV。如果你使用像 Genesis 或 Themeforest 上的某些预构建主题,你可能不会了解 Craft 的意义,但任何自己编写 HTML 和 CSS 并且关心 CMS 输出什么标记的人,都会喜欢 Craft,IMO。
查看我一直在开发的东西
https://github.com/imranismail/rapido
嗨 Imran,感谢你分享这个!我真的很喜欢它。
直接使用 MODX 就可以了,它已经拥有这些功能多年了…
http://modx.com/
“直接使用 _______”
我已经使用 Symfony 一段时间了,你们中的许多人知道 Twig 是那里的首选模板引擎。Twig 非常强大且灵活,提供了很多自由。
但问题在于:它太强大了。它可以做很多事情,它可以做太多事情。你可以在 Twig 的模板中添加逻辑。它很容易被误用。
如果你仔细想想,那么是什么阻止你直接使用 PHP 来完成它的本意,即模板化页面?为什么还要添加一个更慢的层?事实上,在 Symfony 中,你可以使用 PHP 而不是 Twig。
我仍然使用 Twig,但我越来越相信像 Handlebars 这样的模板语言应该足以满足每个人的模板需求。
你好,文章很棒,有一个关于性能方面的问题,Timber会让WordPress运行速度变慢吗(尤其是在共享服务器上),还是几乎没有影响,还有关于内存消耗,它会增加太多吗?还是基本上保持一致?
嗨,Jairo,
谢谢!就我个人而言,使用Timber时没有注意到任何性能下降。插件作者在性能部分的文档中详细介绍了,其中指出性能几乎没有下降。
当你使用一个缓存插件来提供静态页面时,所有这些问题都变得不那么重要了,从而绕过了Twig的渲染过程。
我无法保证所有服务器都一样,只能说我自己的经验,到目前为止一直都很好。
我也很喜欢Timber。我主要使用Timber和branch来开发主题,而且我是branch的开发者,它是一个基于Timber + Bootstrap的WordPress入门主题。
请务必查看一下。
感谢这篇文章。我认为使用Twig的一个优势是,你的大部分模板代码可以重复利用到具有类似语法的模板语言,比如Mustache、Liquid或Swig。