选择模板系统

简介

承认吧,你写过模板系统。这很正常,几乎每个人在某个时候都这样做过。你从一个简单而美丽的东西开始,比如 $HTML =~ s/\$(\w+)/${$1}/g,然后添加条件语句、循环和包含,最终创建了一个自己的难以维护的怪物。

幸运的是,你不是第一个想到把HTML从代码中分离出来的人。很多人在之前这样做过,其中一些人已经将他们的贡献上传到了CPAN。到现在为止,CPAN上已经有如此多的模板模块,几乎可以肯定你能找到一个满足你需求的模块。本文旨在成为你的向导,引导你走向你梦想中的模板系统。

而且,如果你一开始就直接去了CPAN,从未费心自己编写,那么恭喜你:你比我们其他人领先了一步。

个人笔记

在mod_perl邮件列表上,没有什么能比声称某种模板方法比另一种更好更快地引发争论。人们会倾向于他们选择过的工具。因此,我提前说明,我有所偏见。我已经从事这个行业有一段时间了,我对什么最适合有看法。我已经尽力在本文中对各种系统的功能进行平衡评估,但你可能很快就能发现我喜欢什么。此外,试图完全无偏见会导致毫无价值的文档,其中不包含任何真实信息。所以请带着一磅盐来对待这一切,如果你认为我在事实错误或遗漏中对该工具不公平,请告诉我。

为什么要使用模板?

为什么要费心使用模板呢?打印语句和CGI.pm对爷爷来说已经足够好了,那你为什么要费心学习一种新的做事方式呢?

外观一致性

用不着天才就能看出,制作一个导航栏模板并在所有页面上使用它,比在所有地方硬编码它要容易管理得多。如果你以这种方式构建整个网站,要使网站的外观和感觉发生全局变化就更容易了。

可重用性

沿着相同的思路,构建一组常用组件可以更容易地创建新页面。

更好地隔离变化

哪个更经常改变:你应用程序的逻辑还是用于显示它的HTML?实际上答案并不重要,只要它是其中之一。模板可以在应用程序逻辑和显示逻辑之间提供一个很好的抽象层,允许一个更新而不影响另一个。

分工 将你的Perl代码与HTML分离意味着,当你的营销部门决定一切都应该变成绿色而不是蓝色时,你不必动手。只需把他们送到隔壁的HTML编码员那里。这是一件美妙的事情,摆脱HTML业务。

即使你们组织中的同一个人编写Perl代码和HTML,你最后也有机会让更多的人并行工作在项目上。

有哪些区别?

在我们查看可用的选项之前,让我们先解释一下它们之间的一些区别。

执行模型

尽管有些人试图对它有所灵活性,但大多数模板系统都期望你使用两种基本执行模型中的一种变体,我将将其称为“管道”和“回调”。在回调风格中,你让模板接管并具有应用程序的控制流程编码在其中。它使用回调到模块或内联Perl代码片段来检索用于显示的数据或执行用户认证等操作。使用此模型的流行系统包括Mason、Embperl和Apache::ASP。

管道风格在标准的CGI或mod_perl处理器中预先完成所有工作,然后决定运行哪个模板并将一些数据传递给它。模板中没有控制流程逻辑,只有展示逻辑,例如,如果这个商品在促销,则显示这个图形。支持此方法的流行系统包括HTML::Template和Template Toolkit。

回调模型对于以发布为导向的网站效果很好,其中页面基本上是文章和列表的混合和匹配集合。理想情况下,一个网站可以被分解为视觉“组件”或足够通用的页面片段,HTML编码人员可以重新组合它们,而无需任何程序员的帮助,以创建完全新的页面类型。

当您必须编写可能导致返回完全不同内容的逻辑时,回调模型可能会变得有些复杂。例如,如果您有一个处理表单输入并根据提交的数据将用户带到不同页面的系统。在这些情况下,很容易编写一个包括和重定向的意大利面式代码,或者将实际上是多个页面的内容放在同一个文件中。

另一方面,回调方法可能导致文件更少(如果Perl代码在HTML文件中),并且对于许多开发者来说感觉更简单直观。这是从静态文件到带有少量内联代码片段的静态文件的一个简单步骤。这也是PHP为何受到新开发者欢迎的部分原因。

管道模型更像是一个传统的模型-视图-控制器设计。以这种方式工作可以提供比在不知道请求开始时需要什么数据的方法更多的性能调整机会。您可以聚合数据库查询,做出更明智的缓存选择等。它还可以促进应用程序逻辑和展示的更清晰分离。然而,这种方法需要更长的时间来启动,因为它是一个更大的概念障碍,并且始终涉及至少两个文件:一个用于Perl代码,一个用于模板。

请记住,许多系统提供了对自定义其执行模型的重大灵活性。例如,Mason用户可以为应用程序逻辑和显示编写单独的组件,让逻辑组件在获取数据后选择运行哪个显示组件。这使得它可以在管道风格中使用。一个Template Toolkit应用程序可以编写为使用简单的通用处理器(如分发中包含的Apache::Template模块),所有应用程序逻辑都通过对象调用或内联Perl放在模板中。这将使用回调风格。

HTML::Template和一些AxKit XML处理器对坚持管道方法相当严格。它们都不提供在HTML格式化阶段回调到Perl代码的方法;您必须在运行模板之前完成工作。这些工具的作者认为这是一个功能,因为它防止开发人员欺骗应用程序代码和展示的分离。

语言

这是模板系统的大问题。这是在Web开发邮件列表上总是引发激烈讨论的问题。

某些系统使用内联Perl语句。它们可能提供一些额外的语义,例如Embperl的运算符用于指定代码的输出是否应该显示,或者Mason的<%init>部分用于指定代码何时运行,但最终,你的模板是用Perl编写的。

其他系统提供一种专门的微型语言,而不是(或加上)内联Perl。这些通常只有足够的语法来处理变量替换、条件和循环。HTML::Template和Template Toolkit是使用这种方法的流行系统。AxKit在两边摇摆,提供了一种(不那么)微型语言XSLT和一种内联Perl方法XPathScript。

以下是一个典型讨论这些方法优缺点的例子

内联:微型语言很愚蠢。我已经知道Perl,它已经足够简单。你为什么要使用不同的东西呢?

微型语言:因为我的HTML编码者不知道Perl,这对他们来说更容易。

内联:也许他应该学习一些Perl。他会得到更多的报酬。

微型语言:无论怎样。你只是想使用内联Perl,这样你就可以通过在模板中放入小漏洞来处理变更请求,而不是更改你的模块。这是糟糕的编码。

内联:这是高效的编码。我可以比你快一半的时间完成数据编辑屏幕,然后我可以回到模板,将所有内联代码放入模块,并让模板调用它们。

微型语言:你可以,但你不会。

内联:那里的象牙塔里冷吗?

微型语言:去写一些VBScript,小样。

等等。

大多数人都会在这场战争中选边站队,并留在这里。如果你是少数尚未决定的人之一,你应该花点时间考虑谁将构建和维护你的模板,这些人有什么技能,什么将使他们最有效地工作。

以下是一个简单模板示例,首先使用内联样式(在这个例子中是Apache::ASP),然后是微型语言样式(Template Toolkit)。此代码检索一个对象并显示其一些属性。两个示例中使用的数据结构相同。首先Apache::ASP

  <% my $product = Product->load('sku' => 'bar1234'); %>

  <% if ($product->isbn) { %>
    It's a book!
  <% } else { %>
    It's NOT a book!
  <% } %>

  <% foreach my $item (@{$product->related}) { %>
    You might also enjoy <% $item->name %>.
  <% } %>

现在Template Toolkit

  [% USE product(sku=bar1234) %]


  [% IF product.isbn %]
    It's a book!
  [% ELSE %]
    It's NOT a book!
  [% END %]

  [% FOREACH item = product.related %]
    You might also enjoy [% item.name %].
  [% END %]

第三种方法基于将HTML文档解析成DOM树,然后操作节点的内容。唯一使用这种方法的是HTML_Tree模块。这个想法与使用微型语言类似,但它不需要任何非标准HTML标签,也不在模板本身中嵌入任何关于循环或条件的逻辑。这很好,因为这意味着你的模板是有效的HTML文档,可以在浏览器中查看,并在大多数标准HTML工具中操作。这也意味着处理模板的人可以在模板中放入占位符数据进行测试,当模板使用时,这些数据将被简单地替换。当需要在模板中使用if/else类型构造时,这种预览能力就会失效。在这种情况下,预览时“if”和“else”的HTML块都会显示出来。

解析器和缓存

这些模板系统的解析器以三种方式之一实现:它们每次都解析模板(“重复解析”),它们解析它并缓存解析后的解析树(“缓存解析树”),或者它们解析它,将其转换为Perl代码并编译它(“编译”)。

将模板编译成Perl的系统可以利用Perl强大的运行时代码评估功能。它们检查模板,从中生成一段Perl代码,并通过eval执行这段代码。之后,后续对模板的请求可以通过在内存中运行编译后的字节码来处理。解析和代码生成步骤的复杂性取决于系统提供的功能,这些功能超出了直接的内联Perl语句。

将模板编译成Perl字节码在第一次请求时较慢,但一旦模板被编译,性能将非常出色,因为模板变成了Perl子程序调用。这与JSP(Java ServerPages)系统使用的相同方法。在具有长时间运行的Perl解释器(如mod_perl)的环境中,这种方法最为有效。

HTML::Template、HTML_Tree和Embperl的2.0 beta版本都使用了缓存解析树的方法。它们将模板解析到各自内部的数据结构中,然后为每个处理过的模板在内存中保持解析结构。在性能和内存要求方面,这与编译Perl的方法类似,但实际上并不涉及Perl代码生成,因此不需要eval步骤。哪种方法更快:缓存解析树还是编译?很难客观衡量,但根据经验证据,编译似乎更快。Template Toolkit在版本1中使用了缓存解析树方法,但在测试显示它提供了显著的速度提升后,版本2切换到了编译方法。然而,正如稍后将讨论的,任何一种方法都足够快。

相比之下,重复解析方法可能听起来较慢。然而,如果要解析的标记足够简单,它可以相当快。使用这种方法的系统通常使用简单的标记,这使得它们可以使用快速简单的解析器。

为什么你会使用这种方法,如果编译有更好的性能呢?好吧,在没有持久Perl解释器(如vanilla CGI)的环境中,这实际上可能比编译方法更快,因为启动成本更低。当Perl解释器在超过一个请求后不会持续存在时,编译系统完成的Perl字节码缓存是无用的。

还有其他原因。编译的Perl代码占用大量内存。如果你有大量独特的模板,它们可以很快地累积起来。想象一下,如果每个使用服务器端包含(SSI)的页面在访问后都必须保留在内存中,将会占用多少RAM。(不用担心,Apache::SSI模块不使用编译,所以它没有这个问题。)

应用框架与仅模板

一些模板工具试图为Web开发中的问题提供一个全面的解决方案。其他工具仅提供模板解决方案,并假设你会将其与其他模块组合起来构建一个完整的系统。

框架中提供的常见功能包括

URL映射

所有框架都提供了一种将URL映射到模板文件的方法。除了类似于处理静态文档的简单映射外,一些框架还提供了一种方法来拦截特定目录内的所有请求进行预处理,或者根据站点的目录结构创建一个对象继承方案。

会话跟踪

大多数交互式网站需要使用某种形式的会话跟踪来将应用状态数据与用户关联起来。一些工具通过为你处理所有cookie或URL拼接,允许你简单地从包含当前用户会话数据的对象或散列中读取和写入,使这个过程变得简单。一个常见的方法是使用Apache::Session模块进行存储。

输出缓存

缓存是许多Web系统良好性能的关键,其中一些工具提供了用户控制的输出缓存。这是Mason和AxKit的主要功能之一。AxKit可以在页面级别进行缓存,而Mason也提供了对页面内组件的细粒度缓存。

表单处理

没有CGI.pm来解析传入的表单数据,你将如何生存?许多这些工具会为你完成这项工作,并使其以方便的数据结构可用。其中一些还验证表单输入,甚至提供“粘性”的表单小部件,在重新显示时保留所选值,或者根据你提供的数据设置默认值。

调试

每个人都知道调试CGI脚本有多么痛苦。模板系统可能会使情况更糟,因为它们会通过生成的代码破坏Perl的行号。为了帮助解决这个问题,一些工具提供了内置的调试支持,包括额外的日志记录或与Perl调试器的集成。

如果你只想使用模板系统,但需要这些其他功能,并且不想自己实现它们,CPAN上有一些工具提供了你可以构建的框架。libservlet发行版提供了一个类似于Java servlet API的接口,与任何特定的模板系统无关。Apache::PageKit和CGI::Application是这种类型的其他选项,但这两个都目前与HTML::Template绑定。OpenInteract是另一个框架,这次与Template Toolkit绑定。所有这些都可以通过相对较小的努力适应你选择的“仅模板”模块。

竞争者

好的,现在你知道了这些工具之间的区别,让我们看看Perl模板系统的顶级选择。这不是一个详尽的列表:我只包括目前正在维护、文档齐全并且已经建立起一个重要用户社区的系统。简而言之,我排除了大约一打不太受欢迎的系统。在本节末尾,我将提到一些不太常用的系统,但可能值得一看。

SSI

SSI是模板系统的鼻祖,也是许多人首次使用的一个,因为它通常是大多数Web服务器标准的一部分。安装mod_perl后,mod_include获得了一些额外的功能。具体来说,它能够使用新的#perl指令进行内联子程序调用。它还可以通过使用Apache::Include模块有效地包含Apache::Registry脚本的输出。

Apache::SSI模块完全使用Perl实现了mod_include的功能,包括额外的#perl指令。使用它的主要原因是为了对另一个处理程序的输出进行后处理(使用Apache::Filter)或添加自己的指令。通过子类化,添加指令很容易。你可能想通过添加循环和其他构造来以这种方式实现一个完整的模板处理器,但鉴于有这么多其他工具,这样做可能不值得麻烦。

SSI遵循回调模型,基本上是一种迷你语言,尽管你可以在#perl指令中偷偷地插入一些Perl代码作为匿名子程序。因为SSI使用重复解析实现,所以可以在大量文件上安全地使用它,而不用担心内存膨胀。

SSI是对于那些只需要简单模板需求的站点的一个很好的选择,特别是那些只想在页面之间共享一些标准页眉和页脚的站点。然而,在确定这种方法之前,你应该考虑你的站点是否最终需要发展成为更具灵活性和功能的东西。

HTML::Mason

http://www.masonhq.com/

马森(Mason)已经存在了几年时间,并建立了一个忠实的追随者群体。它最初被创建为一个Vignette StoryServer一些最有趣功能的Perl克隆,但后来已经发展成为自己的独特产品。它源自出版背景,包括将页面拆分成可重复使用的“组件”或“块”的功能。

马森使用内联Perl和编译方法,但有一个功能可以帮助将Perl代码从HTML编码者的道路上移开。组件(模板)可以在文件末尾包含一段Perl代码,该代码被特殊标签包裹,表示它应该在模板的其他部分之前运行。这允许程序员将组件的所有逻辑都放在底部,远离HTML,然后在HTML中使用短的内联Perl代码片段来插入值、遍历列表等。

马森是一个网站开发框架,而不仅仅是模板工具。它包括一个方便的缓存功能,可以用来捕获组件的输出或简单地存储计算成本高昂的数据。它是目前唯一提供这种缓存作为内置工具的工具。它还实现了一个参数解析方案,允许组件指定它期望从另一个组件或URI查询字符串传递的名称、类型和默认值。

尽管文档主要演示了回调执行模型,但可以使用马森以管道风格使用。这可以通过多种方式实现,包括构建特殊组件,称为“自动处理程序(autohandlers)”,在特定目录树中的请求之前运行。自动处理程序可以执行一些处理并设置用于包含最小内联Perl的显示模板的数据。它还支持面向对象的网站方法,将继承等概念应用于网站目录结构。例如,/store/book/中的自动处理程序组件可能会继承来自/store/的标准布局,但覆盖背景颜色和导航栏。然后/store/music/可以以不同的颜色做同样的事情。这对于开发大型网站是一个强大的范例。请注意,这种继承仅在自动处理程序组件定义的方法级别上受支持。您不能使用/store/book/foo.html中的另一个组件覆盖/store/foo.html组件。

马森的调试方法是通过创建“调试文件”来在Web服务器环境之外运行马森,提供伪造的Web请求并激活调试器。如果您在使用mod_perl的情况下遇到Apache::DB行为问题或使用不提供内置调试器支持的执行环境时,这可能会很有帮助。

另一个独特的功能是能够将大型模板中的静态文本部分留在磁盘上,并在需要时通过文件查找来拉取,而不是将它们保持在RAM中。这以一些速度为代价,在处理主要是静态文本的模板时,可以显著节省内存。

这个软件包中还有许多其他功能,包括HTML输出过滤和页面预览实用程序。会话支持不是内置的,但包含了一个简单的示例,展示了如何与Apache::Session集成。马森的功能集可能对新手来说有点令人不知所措,但高质量文档和有帮助的用户社区会走很长的路。

HTML::Embperl

http://perl.apache.org/embperl/

Embperl一开始就确定了其语言选择:嵌入式Perl。它是最受欢迎的内联Perl模板工具之一,并且存在时间比大多数其他工具都要长。它以其速度和易用性而享有良好的声誉。

它通常以回调风格使用,Embperl拦截URI并处理请求的文件。然而,它也可以通过从另一个程序中的子程序调用来可选地调用,允许它以管道风格使用。模板被编译成Perl字节码并缓存。

Embperl已经存在了很长时间,拥有令人印象深刻的特性列表。它可以在安全隔离区运行代码,支持自动清理全局变量以简化mod_perl编程,并拥有包括能够将错误邮件发送给管理员在内的广泛调试工具。

Embperl与其他内联Perl系统的最大区别在于其紧密的HTML集成。它可以识别TABLE标签并自动遍历它们的长度。它自动提供粘性表单控件。在HREFSRC属性中的查询字符串末尾放置数组或哈希引用将自动扩展为“name=value”格式的查询字符串。将META HTTP-EQUIV标签转换为真正的HTTP头。

人们喜欢Embperl的另一个原因是它使Web应用程序编码的一些常见任务变得非常简单。例如,所有表单数据只需通过读取魔法变量%fdat即可总是可用。会话支持同样简单,只需通过读取和写入魔法%udat哈希即可。还有一个用于存储持久应用状态的哈希。HTML转义是自动的(尽管可以打开和关闭)。

Embperl包含一个名为EmbperlObject的东西,允许您以类似于上述Mason自动处理程序和继承功能的类似方式将OO概念应用于网站层次结构。这是一种方便地编写具有不同区域样式的网站的编程方式,值得检查。EmbperlObject包括在文件级别进行覆盖的能力。这意味着您可以有一个像/store/music这样的目录,它可以覆盖特定模板并从父目录继承其余部分。

Embperl旧版本的一个缺点是,在将非Perl部分包装起来时,必须使用大多数Perl控制结构(如“if”和“foreach”)的内置替换。例如

  [$ if ($foo) $]
    Looks like a foo!
  [$ else $]
    Nope, it's a bar.
  [$ endif $]

这些在基于内联Perl的系统上可能看起来不合适。截至版本1.2b2,现在可以使用Perl的标准语法

  [$ if ($foo) { $]
    Looks like a foo!
  [$ } else { $]
    Nope, it's a bar.
  [$ } $]

在撰写本文时,Embperl的新2.x分支正在beta测试中。这包括一些有趣的功能,如更灵活的解析方案,可以根据用户口味进行修改。它还支持直接在Embperl模板上使用Perl调试器,并提供性能改进。

Apache::AxKit

http://axkit.org/

AxKit是第一个从地面开始围绕XML构建的mod_perl页面生成系统。技术上,AxKit不是一个模板工具,而是一个用于连接不同模块的框架,这些模块生成和转换XML数据。事实上,它可以选择性地使用Template Toolkit作为XML转换语言。然而,它值得在这里介绍,因为它也是一些模板工具的家园。

在其最简单形式中,AxKit将XML文件映射到XSL样式表,它可以使用常见的XSLT模块(如XML::XSLT或XML::Sablotron)进行处理。将样式表映射到请求的规则是灵活的,它们可以包含查询字符串、cookie和其他请求属性。想法是您可以通过选择正确的样式表来处理具有不同显示能力的广泛客户。

认识到并不是每个人都喜欢XSL有些晦涩难懂的语法,Matt Sergeant提供了一种名为XPathScript的替代样式表语言。XPathScript允许您使用嵌入Perl代码的文本编写样式表。这与其他嵌入Perl模板工具类似,但重点是使用内置的XPath函数来查询XML文档并操作检索到的数据。XPathScript还可以用声明性方式使用,指定XML输入中特定元素的外观格式。例如,此代码片段将XML文档中的所有<foo>标签在输出中更改到BAR:

  <%
    $t->{'foo'}{pre}   = 'BAR';
    $t->{'foo'}{post}    = '';
    $t->{'foo'}{showtag} = 0;
  %>
  <%= apply_templates() %>

通过使用XPathScript的include函数(它看起来就像SSI),您可以构建使用此技术的方法库。

如果您在磁盘上有一堆XML文件,那么一切都很顺利,但是动态内容怎么办?AxKit通过允许您用不同的数据源替换默认的基于文件的数据源来处理这个问题。这可以包括在每个请求上运行一些动态代码来生成将被转换的XML数据。这个发行版包括一个名为XSP的模块来执行此操作。XSP是一种使用内联Perl和标签库构建XML DOM的语言。标签库被指定为可以将XML标签转换为Perl代码的样式表。这通过包含的SQL标签库得到演示,它允许您使用XML标签编写一个XSP页面,这些标签将连接到数据库,执行查询并生成包含结果的XML文档。

AxKit内置了一些性能提升。它可以缓存页面的完整输出并将其作为静态文件在未来的请求上提供服务。它还可以对输出进行压缩以加快理解gzip编码的浏览器的下载速度。这些可以通过其他系统完成,但需要您设置额外的软件。使用AxKit,您只需在配置文件中启用它们即可。

如果所有这些语言、标签库和样式表让您感到害怕,那么AxKit可能对您的项目来说有些过于强大。然而,AxKit的优势在于它是基于批准的W3C标准的,并且许多用于其开发中的技能可以转移到其他语言和工具。

Apache::ASP

http://www.apache-asp.org/

Apache::ASP最初是Microsoft的Active Server Pages技术的移植,其基本设计仍然遵循该模型。它使用内联Perl和编译方法,提供一组简单的对象来访问请求信息并生成响应。为Microsoft的ASP编写使用Perl(通过ActiveState的PerlScript)的脚本通常可以在该系统上运行而无需更改。(不支持用VBScript编写的页面。)

与原始ASP一样,它有在特定事件触发时调用指定代码的钩子,例如新用户会话的开始。它还提供了相同的易于使用的状态和会话管理。存储和检索整个应用程序或特定用户的状态数据就像一个方法调用一样简单。它甚至可以支持无cookie的用户会话——这是这些系统中的独特功能。

来自Microsoft ASP的一个显著添加是XML和XSLT支持。提供了两种选项:XMLSubs和XSLT转换。XMLSubs是一种向您的页面添加自定义标签的方法。它将XML标签映射到您的子程序,因此您可以将类似<site:header page="Page Title" />这样的内容添加到您的页面中,并将其转换为类似&site::header({title => "Page Title"})的子程序调用。它还可以处理带有正文文本的XML标签。

XSLT支持允许ASP脚本的输出通过XSLT进行过滤以进行展示。这允许您的ASP脚本生成XML数据,然后使用单独的XSL样式表格式化该数据。这种支持是通过与XML::XSLT模块的集成提供的。

Apache::ASP通过使用HTML::FillInForm模块提供表单的粘性小部件。它还内置了对从生成输出中删除多余空白的支持、对支持gzip的浏览器进行gzip压缩输出、使用Time::HiRes跟踪性能、自动将错误消息发送给管理员以及许多其他便利性和调整选项。这是一个成熟的包,它已经发展到能够处理现实世界的问题。

关于这个系统中的会话和状态管理需要注意的一点是,它目前仅通过使用NFS或SMB等网络文件系统来支持集群。(该模块的作者Joshua Chamas报告说,Samba文件共享比NFS的结果要好得多。)这可能是一个问题,因为大型服务器集群通常依赖于关系型数据库来存储会话的网络安全存储。计划在未来的版本中支持会话的数据库存储。在此期间,提供了连接到Apache::Session的说明。

Text::Template

Text::Template

该模块已成为CPAN上事实上的通用模板模块标准。它具有简单易用的接口和详尽的文档。文档中的示例展示了管道执行风格,但编写一个直接调用模板的mod_perl处理器的回调风格也很容易。该模块使用内联Perl。它能够在安全组件中运行内联代码,以防您担心代码错误会导致服务器崩溃。

该模块依靠内联代码的创造性使用来提供人们通常期望从模板工具中获得的功能,如包含。这可能是好是坏。例如,要包含一个文件,您只需调用Text::Template::fill_in_file(filename)。然而,您必须指定完整的文件路径,而且没有任何东西可以阻止您使用/etc/passwd作为要包含的文件。大多数更复杂的模板工具都有诸如包含路径等概念,允许您指定要搜索包含文件的目录列表。您可以编写一个以这种方式工作的子程序,并将其放在模板命名空间中,但这不是内置的。

每个模板都被加载为一个单独的对象。模板被编译为Perl,并且仅在第一次使用时进行解析。然而,为了充分利用像mod_perl这样的持久环境中这种缓存,您的程序必须跟踪已使用的模板,因为Text::Template没有全局跟踪此内容并在可能的情况下返回缓存模板的方法。

Text::Template不绑定到HTML,它只是一个模板模块,而不是Web应用程序框架。它可以完美地生成电子邮件、PDF等。

模板工具包

http://template-toolkit.org/

模板工具包是最近加入模板场景的更多新增之一,是一个灵活的迷你语言系统。它有一套完整的数据操作指令,包括循环和条件语句,并且可以通过多种方式扩展。可以通过配置选项启用内联Perl代码,但通常不建议这样做。它使用编译,将编译后的字节码缓存到内存中,并且可选地将生成的Perl代码缓存到磁盘上的模板中。尽管它通常以管道风格使用,但包含的Apache::Template模块允许模板直接从URL调用。

模板工具包具有丰富的功能集,所以我们只能在这里概述一些亮点。TT发行版在文档的详尽性和质量上树立了黄金标准,因此如果您选择的话,很容易了解更多信息。

TT与其他系统之间一个主要区别是它通过点操作符的概念提供了对复杂数据结构的简单访问。这使得不了解Perl的人也可以访问嵌套列表和散列或调用对象方法。例如,我们可以传递这个Perl数据结构

  $vars = {
           customer => {
                        name    => 'Bubbles',
                        address => {
                                    city => 'Townsville',
                                   }
                       }
          };

然后我们可以在模板中引用嵌套的数据

  Hi there, [% customer.name %]!
  How are things in [% customer.address.city %]?

这比Perl中等效的语法更简单、更统一。如果我们作为数据结构的一部分传递一个对象,我们可以使用相同的表示法调用该对象内的方法。如果您已将系统的数据建模为一组对象,这将非常方便。

模板可以定义宏和包含其他模板,并且可以传递参数到其中。包含的模板可以选择性地本地化它们的变量,以便在包含的模板执行期间所做的更改不会影响更大作用域中变量的值。

有一个过滤器指令,可以用于后处理输出。其用途从简单的HTML实体转换到自动截断(当您想限制条目的大小时的下拉菜单很有用)以及打印到STDERR。

TT支持插件API,可以用来为模板添加额外功能。提供的插件可以大致分为数据访问和格式化。标准的数据访问插件包括访问XML数据或DBI数据源的模块,并在模板中使用这些数据。还有一个插件可以访问CGI.pm。

格式化插件允许您以本地化的方式显示日期和价格等。还有一个用于在多列格式中显示列表的表格插件。这些格式化插件很好地解决了数据显示的最终5%问题,这些问题通常会导致使用内部系统的人在他们自己的Perl模块中嵌入一点HTML。

在类似的方向上,TT为模板编写者提供了一些方便的特性,包括消除标签周围的空白和更改标签分隔符——这些可能听起来有些晦涩,但有时可以使模板的使用变得更加容易。

TT发行版还包括一个名为ttree的脚本,它可以处理整个模板目录树。这对于预先发布模板页面并以静态方式提供它们的网站很有用。脚本检查修改时间,并且仅更新需要更新的页面,提供类似make的功能。发行版还包括一组模板驱动的HTML小部件样本,可用于为文档集合提供一致的样式和感觉。

HTML::Template

HTML::Template

HTML::Template是那些想使用迷你语言而不是内联Perl的人中很受欢迎的一个模块。它使用一组简单的标签,允许循环(甚至是在嵌套的数据结构中)和条件,以及基本的值插入。标签的样式故意设计得像HTML标签,这在某些情况下可能很有用。

正如文档所说,它“只做一件事,而且做得很迅速、很仔细”——它不尝试添加应用程序功能,如表单处理或会话跟踪。该模块遵循管道执行风格。解析后的模板存储在Perl数据结构中,可以在任何组合的内存、共享内存(使用IPC::SharedCache)和磁盘中进行缓存。文档完整且编写得很好,提供了大量示例。

您可能想知道这个模块与另一个流行的迷你语言系统Template Toolkit有何不同。除了明显的语法差异之外,HTML::Template速度更快,更简单,而Template Toolkit具有更多高级功能,如插件和点表示法。这里有一个简单的示例比较语法

HTML::Template

  <TMPL_LOOP list>
      <a href="<TMPL_VAR url>"><b><TMPL_VAR name></b></A>
  </TMPL_LOOP>

模板工具包

  [% FOREACH list %]
      <a href="[% url %]"><b>[% name %]</a></a>
  [% END %]

现在,让我们提一下几个值得注意的项目

HTML_Tree

http://homepage.mac.com/pauljlucas/software/html_tree/

如前所述,HTML Tree使用一种相当独特的方法进行模板化:它加载一个HTML页面,将其解析为DOM,然后以编程方式修改节点的内容。这使得它可以使用真正的有效HTML文档作为模板,这是这些其他模块所不能做到的。学习曲线比平均水平略高,但如果您担心为HTML编码者保持简单,这可能正是您所需要的。请注意,名称是“HTML_Tree”,而不是“HTML::Tree”。

Apache::XPP

http://opensource.cnation.com/projects/XPP/

XPP是一个编译为字节码的内联Perl系统。虽然它是一个非常好的实现,但它几乎没有差异,除了一个定义新HTML-like标签的简单机制,这些标签可以用来替换模板中的内联代码。

ePerl

Apache::ePerl

可能是第一个将Perl代码嵌入文本或HTML文件中的模块,ePerl现在以Apache::ePerl的形式仍然是一个可行的选项。它通过在内存中缓存编译的字节码来实现良好的性能,有些人发现它使用起来令人耳目一新地简单。

CGI::FastTemplate

CGI::FastTemplate

本模块采用极简主义方法进行模板化,这使得它在CGI程序中使用时非常出色。它使用单个正则表达式解析模板,且不支持模板中的任何复杂功能,仅支持简单的变量插值。循环通过包含其他模板的输出进行处理。遗憾的是,这导致了一种比大多数Perl代码更难以理解的编码风格,并且模板文件数量激增。然而,有些人坚信这种极其简单的做法。

性能

人们总是担心模板系统的性能。如果您曾经构建过大型应用程序,您应该对不同操作的相对成本有足够的了解,知道模板系统不是寻找性能提升的首选地方。这里提到的所有系统在mod_perl等持久执行环境中都具有出色的性能特征。与从数据库或文件中获取数据等缓慢操作相比,模板系统增加的时间几乎可以忽略不计。

如果您认为您的模板系统拖累了您的速度,那么获取事实:取出Devel::DProf并查看。如果这里提到的工具之一在列表顶部,那么您应该为自己鼓掌——您已经出色地调整了系统并消除了瓶颈!就我个人而言,我只在成功缓存了处理请求的几乎所有部分(除了运行模板)时看到过这种情况。

然而,如果您确实处于需要从页面生成时间中挤出几微秒的情况,系统之间确实存在性能差异。它们基本上是您所期望的:执行最少的工作的系统运行得最快。使用内联print()语句比使用模板更快。使用简单替换比使用内联Perl代码更快。使用内联Perl代码比使用小程序言更快。

目前可用的唯一模板基准测试是由Apache::ASP的作者Joshua Chamas开发的。它包括一个“hello world”测试,它简单地检查每个系统返回这些著名单词的速度,以及一个“hello 2000”测试,它测试了大多数动态页面使用的基本功能。它可以从以下URL获取:

http://www.chamas.com/bench/hello.tar.gz

该基准测试的结果目前显示SSI、Apache::ASP和HTML::Embperl具有最佳性能。这里提到的并非所有系统都包含在测试中。如果您的首选工具被遗漏,您可能需要下载基准测试代码并添加它。正如您所想象的那样,对人们的宠儿项目进行基准测试是一项几乎得不到回报的任务,Joshua为社区做出了贡献,理应得到一些认可和支持。

CGI性能关注点

如果您在CGI下运行,您有更重要的事情要做,而不是担心模板系统的性能。尽管如此,有些人仍然处于CGI状态,但仍然想使用性能合理的模板系统。CGI是一个棘手的情况,因为您必须担心Perl在每次请求中编译大型模板系统代码所需的时间。CGI还破坏了这些系统大多数使用的模板的内存缓存,尽管Mason、HTML::Template和Template Toolkit提供的较慢的基于磁盘的缓存仍然有效。(HTML::Template确实提供模板的共享内存缓存,这可能有助于提高性能,尽管在我的Linux系统上,共享内存通常比使用文件系统慢。欢迎提供基准测试和更多信息。)

与CGI一起,您的最佳性能选择是使用一些简单的工具,如CGI::FastTemplate或Text::Template。它们体积小,编译速度快,CGI::FastTemplate由于依赖于简单的正则表达式解析且不需要eval任何内联Perl代码而获得额外提升。这里提到的几乎所有其他内容都将在编译时间上为每个页面增加十分之一秒。


矩阵

为了帮助您选择系统,我将总结文章开头所解释的决策点上的主要系统的基本特征。请注意,在许多情况下,一个系统可以以多种方式使用,我仅仅展示了在文档和现实世界使用中看到的占主导地位的方法。您在阅读上面的更详细说明之前,不要根据此图表排除选项。

应用框架 流水线或回调 解析方法 语言
HTML::Mason 框架 回调 编译 Perl
模板工具包 仅模板 流水线 编译 迷你语言
Apache::ASP 框架 回调 编译 Perl和XSL
HTML::Embperl 框架 回调 编译 Perl
SSI 仅模板 回调 重复解析 迷你语言
AxKit 框架 流水线 编译或缓存解析树 Perl、XSL和迷你语言
HTML::Template 仅模板 流水线 缓存解析树 迷你语言
Text::Template 仅模板 流水线 编译 Perl

更新

这些模块是移动的目标,这份文档必然包含一些错误。请将您的更正发送到[email protected]此文档的将来版本将在mod_perl邮件列表上公布,可能还会在其他流行的Perl位置上公布。

标签

反馈

这篇文章有什么问题吗?请通过在GitHub上打开一个问题或拉取请求来帮助我们。