Twig:加速其模板的生成
最近,我问自己反思 Twig 提供的访问对象或数组属性的解决方案。
访问对象的属性
Twig 旨在简化我们的模板,无论是在代码方面还是在清洁方面。 它还旨在允许集成商,即不具备开发知识的人,轻松访问对象或其他对象的属性。 这要归功于简化的语法。
直到那时,首先作为一名开发人员,我问自己一个问题,即 twig 是如何确定应该调用对象的哪个方法的。
Twig 语法
在我的以下代码中,我将假设我正在使用如下所示的类。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
程 摆件
{
私立 $姓名;
国家 $用户名;
国家 功能 获取名称() {
回报 $这->名称;
}
国家 功能 获取用户名() {
回报 $这->用户名;
}
}
|
这就是我从 Symfony 控制器调用模板的方式。
1
2
3
|
国家 功能 索引操作() {
回报 排列(“目的” => $对象);
}
|
到目前为止,一切都很清楚。 我的对象看起来与我可以在我的项目中使用的对象完全一样。
在 Twig 中解析声明
1
|
{{ object.name }}
|
从现在开始,我们将看到 Twig 如何处理我们的调用。 在这里,我们要求 Twig 取值 姓名 在我的对象中,除了 Twig 最后只是一个简单的 PHP 调用之外,这个变量是不可访问的。 因此,Twig 添加了一个代理来抽象并查看可用的属性和方法。
因此,Twig 将按照以下顺序要求对该对象进行检查。
- 看看 对象 是一个数组,如果 姓名 是一把钥匙;
- 看看 对象 是一个对象,如果 姓名 是一个可访问的属性;
- 看看 对象 是一个对象,如果 姓名 是一种方法;
- 看看 对象 是一个对象,如果 获取名称 是一种方法;
- 看看 对象 是一个对象,如果 名称 是一种方法。
在这里,使用我们使用的符号,我们必须等待第 4 个条件到达我们的元素。 因为 姓名 不是可访问的属性,也不是方法。1{{ object.username }}
在这种情况下,我们试图访问 用户名. 这个属性是一个公共属性(在 Symfony 中,我很少遇到这种情况)。 我们必须等待第二个条件。
1
|
{{ object.getName() }}
|
在我们的第三种情况下,我尝试直接调用我的方法。 我从第三个条件得到我的结果,这是一个更快的条件。
1
|
{{ object.getUsername() }}
|
在我们的第四个也是最后一个案例中,我尝试直接使用我的方法。 我从第三个条件得到我的结果。 在这种情况下,我添加了一个附加条件来访问我的值。
解释 Twig 模板
当我看到这个细节时,我想到了 Twig 的开发者是如何实现模板缓存和编译的。 没过多久我就得出结论,利用模板管理器提供的少量信息和自由,我们的文件可以简单地以类似于我们完全可以做的方式转录成 PHP。 您也可以通过查看缓存文件夹来亲自查看。
1
2
|
{# 模板分支#}
{{ object.name }}
|
1
|
echo twig_escape_filter($this->env, $this->getAttribute((isset($context["object"]) ? $context["object"] : $this->getContext($context, "object")), “名称”),“html”,空,真);
|
第一个块匹配我在我的树枝模板中记下的内容,第二个块匹配我在缓存文件夹中找到的翻译版本。
我们注意到该分支已被翻译成 PHP,但没有元素允许它准确预测应该调用哪个方法。 方法 branch_escape_filter 可以比作代理。 正是在这个方法中,我们将确定我们如何访问属性。
结论
尽管 Symfony 自动缓存了 twig 模板,但只保留了 PHP 版本,而没有解释您要检索的内容。 因此,理论上,这里有优化调用和模板生成时间的方法,因为验证是在每次调用时进行的。
基准
我仍然想了解通过调用方法而不是“ 别号 “。
在第一种情况下,我调用一个模板,该模板将调用同一个对象 10 次,而该对象又调用 25 个别名。 这代表 250 个调用。 结果以 10 为单位放大,以便准确计算性能增益。
其次,我调用一个模板,它将调用同一个对象 10 次,然后调用 25 个方法(总是通过与别名相同的代理)。 又是250。
我分别调用了这些模板 5 次。
请注意,对模板进行方法调用的所有调用都更快。 通过取平均值,我们注意到使用别名的模板要长 54,6 毫秒 (197.4 – 142.8)。
通过快速计算,我们注意到,如果将其简化为一般情况,使用方法调用的模板在相同数据上的平均速度提高了大约 26.7%。 这在对对象进行多次调用时会很有趣。
这第二篇文章紧跟第一篇文章,给出了优化模板生成的快速解决方案。 这是已经在延迟率过高的生产站点上使用的优化的结果。
我们都用 包括 de 嫩枝 改变或考虑我们的观点。 但也许你永远不明白为什么我们有传递参数的能力,当一个基本的包含从当前上下文继承变量时。
1
|
{% 包括 “ProjectMyBundle:Sub:template.html.twig” %}
|
我们经常使用这种表示法。 在这种情况下,include 会将我们所有变量的副本提供给我们的模板。 这并不总是必要的,我们复制所有变量是错误的。
“唯一”选项的兴趣
我想了很久这个选项的意义何在。 此外,必须说文档没有提供太多信息。 您可以查看 Twig 包含文档。 它提供的元素很少,但它特别有缺点,没有优点:没有某些变量。 这样看来,是无利可图的; 为什么我应该没有一些变量。
让我们实现我们的包含! 假设我们的基本模板有 5 个变量,我包含一个只使用其中一个变量的模板,这是更可取的表示法。
1
|
{% 包括 "ProjectMyBundle:Sub:template.html.twig" with {"object": myVar} only %}
|
这样,我的模板中将只有一个“对象”变量可用。 这限制了无用变量的复制,因此限制了内存分配(节省时间和内存消耗)。
代码和用例
1
2
3
4
5
6
|
// 控制器
// 这里通过传递包含给定表的所有对象的强加变量(集合)生成模板
国家 功能 测试动作() {
$对象= $这->容器->获取(“doctrine.orm.default_entity_manager”)->获取存储库(“ProjectMyBundle:测试”)->查找全部();
回报 排列(“项目” => $对象);
}
|
我们遍历我们的集合,并将集合的每次迭代都提供给模板。 在这种情况下,每次我们调用模板的集成时,所有的变量都会被复制 “与”选项. 在我们的第一个案例中,我们可以很好地想象访问我们的集合(对象)以及我们当前的迭代(对象)。
1
2
3
4
5
|
{#模板1#}
{% 。 对象中的对象 %}
{% 包括 "ProjectMyBundle:Sub:template.html.twig" 与 {"object": object} %}
{% 终极 %}
|
在我们的第二种情况下,我激活该选项 仅由 这允许您仅复制作为参数传递的变量。 简单的 ?
1
2
3
4
5
|
{#模板2#}
{% 。 对象中的对象 %}
{% 包括 “ProjectMyBundle:Sub:template.html.twig”只有 {“object”: object} %}
{% 终极 %}
|
基准
我使用上面部分给出的模板和代码进行了测试。 我对每个模板进行了 5 次迭代,记得在每次第一次测试之前清空缓存。
通过这张图,我们可以看到对于那些不使用该选项的人来说,加载时间通常更长 仅由. 缓存后差异趋于减小。 这是因为我的模板很小,使用的变量很少。 在更多的应用案例中,有高达30%的增益。
在这里,如果我们对这些值进行平均,则平均差为 9 毫秒 (48,6 – 39,6 = 9)。 因此,即使在我们的案例中考虑到这一点,我们也可以计算出大约 20% 的增益,因为如果不使用“ 仅由 “。