2

我的 Web 应用程序当前使用 Tapestry 5.2.6。我想编写一个具有以下要求的新功能:

  • 用户可以单击图库中的项目以查看显示详细视图的灯箱,包括描述、评论、卖家控制(如果他们拥有正确的凭据)以及无需离开页面即可购买的能力。
  • 应该更新 url 以反映它们是在画廊视图还是在详细信息视图中。
  • 在支持 HTML5 pushState 的浏览器中,URL 更改应该是动态的。在旧版浏览器中可以接受整页刷新。
  • 图库页面和详细信息页面都必须是可抓取的 - 没有 Javascript 的用户应该看到一个完全标记的页面。
  • 速度 - 需要比我对 Tapestry 的预期要快得多。

我的计划是选择并实现一种模板语言,它可以在服务器或客户端上进行同样的评估。对于初始页面加载,我可以在服务器上呈现模板。对于后续更新,我可以将项目的 viewmodel 对象作为 JSON 传递给客户端并在那里评估模板。

到现在为止还挺好。问题是我看过的所有模板语言都没有强大到足以让我对未来的发展感到满意。作为案例研究,请从以下方面考虑:

  • 胡子
  • 灰尘
  • 霍根
  • 车把

似乎没有人有能力进行这样的“包装”转换:

# base template
{>widget}
    <span class="content">Hello world</span>
{/widget}

# widget template
<div class="widget">
    {>widget_body/}
</div>

# rendered output
<div class="widget">
    <span class="content">Hello world</span>
</div>

请注意,包装的内容取自基本模板,小部件模板的输出在两侧围绕它。我知道在上述语言中实现这一点的唯一方法是一个类似的模板:

{>open_widget/}
    {>widget_body/}
{>close_widget/}

这意味着每个组件都有两个模板,一个开启者和一个关闭者,都包含未封闭的标签。(公平地说,dust 可以使用块和内联部分优雅地做到这一点,但因为内联部分对模板是全局的,所以每个模板只能使用一次小部件。)

我对模板的问题是:

  • 我知道像 LinkedIn 和 Twitter 这样的行业领导者正在使用这些技术并且做得很好。我要求太多了吗?如果您使用过其中一种,您是如何处理“包装问题”的?
  • 我研究过的一些解决方案似乎支持它:jquery-tmpl,它不再被官方维护;underscore 和 ejs,作为嵌入式代码的长期解决方案让我感到紧张;和闭包模板。目前 Closure 在我看来是最好的,令我惊讶的是!如果您使用过其中任何一种,您的发现是什么?
4

3 回答 3

1

我不确定外面的东西是否符合你所说的。我需要类似的东西,并认为编写一个简单的文本替换脚本会比比较现有解决方案和学习使用一个更快。

该脚本尚未准备好生产(应该进行更多测试,并且 API 很奇怪),但它应该让您了解一种可以完成的方法。

这是它的设置方式:


在文档中存储模板

模板文本存储在具有type“text/javascript”以外的属性的脚本标记中。每个模板都有一个唯一的 id 属性。

<script id="some_template" type="text/plain">

    a valid template

</script>

浏览器不应该呈现这些。允许使用任何字符(除了</script>),无需转义。


占位符

占位符如下所示:{@some_identifier}.

<script id="image_template" type="text/plain">

    <a href="{@img_url}"><img src="{@img_url}"></a>

</script>

每个占位符将替换为:

  • 从另一个模板传入的值,
  • 获取模板副本时传递给 JavaScript 函数的参数,或
  • 如果没有找到替换值,则为空字符串。

在另一个模板中包含一个模板

@@伪标签”包括当前模板中另一个模板的内容。

<script id="photo_template" type="text/plain">

    <@@ image_template></@@>

    <div class="photo-caption">{@caption}</div>

</script>

photo_template包括 image_template. 所有包含替换发生在任何占位符替换之前,photo_template{@img_url}占位符也是如此{@caption}


包含占位符替换

这就是“包装”的来源。理想情况下,占位符几乎总是被其他模板中的内容替换,而不是在获取模板副本时传入的值。

<script id="missing_photo_template" type="text/plain">

    <@@ photo_template>
        <@ img_url>notfound.png</@>
    </@@>

</script>

missing_photo_template包括photo_template,为它提供一个替代品{@img_url},所以missing_photo_template只有{@caption}占位符。


JavaScript

API 现在很烂,但基本上主命名空间at有两个函数,txtnode. 第一个获取模板副本作为文本,第二个获取副本作为元素(这意味着它应该有一个根节点,与我上面的一些示例不同)。

这里是:

/**

    Atrocious Templates

*/
var at = (function(){

  var rTemplate = /<@@\s*(.*?)>((?:[\w\W](?!<@@))*?)<\/@@>/gm,
      rOption = /<@\s*(.*?)>([\w\W]*?)<\/@>/gm,
      rValue = /\{@(.*?)\}/g,
      rTag = /<(\w+)/i,
      rSpace = /\s+/,
      templates = {},
      doc = document.implementation.createHTMLDocument('');

  /** 

      Inlcude inner templates.

      @private

      @param {string} m0
          The full inclusion text.

      @param {string} m1
          The ID of the included template.

      @param {string} m2
          Values passed to included template.

      @return {string} 

  */
  function includeTemplates(m0, m1, m2) {
    var opts = {};
    m2.replace(rOption, function(m0, m1, m2) { opts[m1] = m2; });
    return txt(m1, opts, true);
  }

  /** 

      Get text contents of a template.

      @private

      @param {string} id
          The ID of the template.

      @return {string} 

  */
  function get(id) {
    if (templates[id]) return templates[id];
    var last, t = document.getElementById(id).innerHTML;
    while (last != t && (last = t)) t = t.replace(rTemplate, includeTemplates);
    return (templates[id] = t);
  }

  /** 

      Get a text copy of a template.

      @param {string} id
          The ID of the template.

      @param {Object.<string|function ():string>} options
          Properties of this object will replace placeholder tokens.
          Each property can be either a string, or a function which 
          returns a string.

      @param {boolean=} noStrip
          By default, placeholders for which no replacement text is 
          found are removed. Setting this to `true` overrides that
          behavior, leaving non-replaced placeholders intact. 

      @return {string} 

  */
  function txt(id, options, noStrip) {
    if (!options) options = {};
    return get(id).replace(rValue, function(m0, m1) {
      var v = options[m1];
      return noStrip && !v && m0 || v && (v.call ? v() : v) || '';
    });
  }

  /** 

      Get a node copy of a template.

      @param {string} id
          The ID of the template.

      @param {Object.<string|function ():string>} options
          Properties of this object will replace placeholder tokens.

      @return {string} 

  */
  function node(id, options) {
    var text = txt(id, options),
        root = text.match(rTag)[1];
    doc.open; doc.write(text); doc.close();
    return doc.getElementsByTagName(root)[0];
  }

  // exports

  return { txt: txt, node: node };

}());

同样,我不建议您在生产中使用它,因为它没有经过太多测试(尽管它似乎工作正常),但希望这会给您一些关于如何实现您想要的想法的想法。

于 2012-04-20T05:50:26.470 回答
0

尝试查看 handlebarjs.com - 我在服务器和客户端上都使用它。

于 2012-11-05T10:51:51.617 回答
-1

也许这个问题很老了,但是 Mustache 不支持partials吗?

http://mustache.github.io/mustache.5.html

...通过这种方式,您可能希望将部分视为包含或模板扩展,即使它不是字面上正确的。

例如,这个模板和部分:

base.mustache

<h2>Names</h2> {{#names}}   {{> user}} {{/names}}

user.mustache

<strong>{{name}}</strong>

可以被认为是一个单一的、扩展的模板:

<h2>Names</h2> {{#names}}   <strong>{{name}}</strong> {{/names}}
于 2014-12-18T20:15:27.880 回答