Node.js与Handlebars.js在服务器和客户端上的应用

36

我有一个使用Expressjs和Handlebars作为模板引擎的Node.js应用程序。

Expressjs使用布局(layouts)来渲染视图。 布局(layout.hbs)如下所示:

<!doctype html>
<html lang="en">
    <head>
    </head>
  <body>
    {{{body}}}
  </body>
</html>

{{{body}}} 在访问路由时会在 node.js 中被服务器端替换。例如:

app.get('/', function(req, res){
   res.render('index'})
})

{{{body}}}标签替换为index.hbs的内容。

现在,在客户端上,我正在使用Backbone.js,并希望在由Backbone控制的视图中使用Handlebars。问题是因为这些页面已经通过Handlebars呈现,当我尝试在其中使用Handlebars(或Handlebars内部)时,它不起作用。没有错误,它只是不会使用数据替换标记。

有人遇到过这种情况或者有任何想法可以解决吗?

谢谢!

7个回答

75

建议使用预编译的客户端模板。它们执行速度更快,并且允许您在服务器和客户端上使用相同的模板语言。

  1. 全局安装handlebars npm install handlebars -g
  2. 预编译模板 handlebars client-template1.handlebars -f templates.js
  3. 引入templates.js文件 <script src="templates.js"></script>
  4. 执行模板 var html = Handlebars.templates["client-template1"](context);

https://dev59.com/tGzXa4cB1Zd3GeqPPxML#13884587


33

一个简单的方法是在Handlebars文件中的{{之前加上\。例如:

<script type="text/x-template" id="todo-item-template">
<div class="todo-view">
    <input type="checkbox" class="todo-checkbox" \{{checked}}>
    <span class="todo-content" tabindex="0">\{{text}}</span>
</div>

<div class="todo-edit">
    <input type="text" class="todo-input" value="\{{text}}">
</div>

<a href="#" class="todo-remove" title="Remove this task">
    <span class="todo-remove-icon"></span>
</a>

上述代码将在客户端呈现,并保留{{..}}标记。


真的吗?该死,我希望我一年前就知道了。这很好而且简单。 - Zachary Yates
1
那个很好用,但我希望他们把它放在文档里……或者可能是我错过了。 - skud
非常感谢,我一整个早上都在试图弄清楚这个问题。 - user752746

14

这是一个棘手的问题,就像shell脚本中的引用问题一样,会变成引用混乱的地方。

我的解决方法是在expressjs(服务器端)中使用jade(类似于haml)来输出基于handlebars的模板。这样,服务器使用一种语法(jade),客户端使用另一种语法(handlebars)。我和你一样陷入了同样的境地,所以我面临着同样的挑战。

当然,jade并不是必需品(尽管它已经为expressjs准备好了)。您可以选择任何(非handlebars)模板引擎用于服务器,并/或者您可以在服务器上使用handlebars,在客户端上使用其他模板引擎,只要您选择的这两个模板引擎的语法不冲突即可。由于我在客户端使用的是emberjs,它使用了handlebars语法(默认情况下),因此我更喜欢在客户端使用emberjs + handlebars语法。因此,expressjs + jade成为服务器的自然选择。


好的,听起来我必须使用另一个模板引擎 - 谢谢! - dzm
虽然使用Jade似乎是解决方案,但我并不完全相信。如果你找到其他的解决方案,我会非常高兴...目前我相信使用Jade和Angular.js是我的救星! - Marco Godínez

11

自我推广!

我想做这个客户端/服务器共享的事情,所以我写了一个小型npm包来协助:

node-handlebars-precompiler

我在wycats的handlebars存储库中的命令行编译器基础上花了几个小时创建了它。它不是世界上最好的代码,但它一直很好地完成我的工作。

编辑:我不再维护此软件包。如果您想接手,请通过Github与我联系。我现在主要使用Jade模板,因此继续担任维护者没有意义。


4

我通过在服务端模板中传递客户端模板来解决了这个问题。

因此,在服务器端将所有客户端模板读入数组,并将其传递到服务器端的渲染函数中。

在路由处理程序中,可以采取以下操作:

readTemplates(function(err, clientTemplates) {
  res.render("page", {
    clientTemplates: clientTemplates;   
  });
});

然后在layout.hbs中:

{{#each clientTemplates}}
<script type="text/handlebars id="{{this.filename}}" >
{{{this.template}}}
</script>
{{/each}}

我在这里使用没有扩展名的文件名作为模板id,以便它们可以从Backbone视图中引用。哦,还记得要在生产模式下实现缓存。

是的,这很糟糕。

我认为我们应该为此编写一个Handlebars/Express/Connect助手程序。


2
我不喜欢预编译解决方案(因为我希望在同一文件中定义模板和使用它们),也不喜欢天真的\{{转义解决方案(因为它需要完整的Handlebars编译器和更多的JavaScript代码),所以我想出了一个混合解决方案,使用Handlebars的helpers:

1)在服务器配置中注册一个名为“template”的新helper。

var hbs = require('hbs');
hbs.registerHelper("template", function(key, options){
    var source = options.fn().replace("\\{{", "{{");
    var ret =
    '<script>\n' + 
        key + ' = function(opt){\n' +
            'return Handlebars.template(' + hbs.handlebars.precompile(source) + ')(opt);\n' +
        '}\n' + 
    '</script>';
    return ret;
});


2) 在客户端网页的任何地方使用它(使用\{{转义客户端参数)

{{#template "myTemplate"}}
    <div>
        <p>Hello \{{this.name}}!</p>
    </div>
{{/template}}

(服务器将会在类似这样的东西中进行预编译)

<script>
    myTemplate = function(opt){
        return Handlebars.template(/* HBS PRECOMPILATED FUNCTION */)(opt);
    }
</script>


3)在客户端 JavaScript 中需要的位置简单地调用该函数即可。

var generatedHtml = myTemplate("world");   // = <div><p>Hello world!</p></div>
$("#myDiv").html(generatedHtml);           // or whatever

1
你有两个选项。第二个是最好的选择:
1)转义胡子符号
<script type="text/x-handlebars" data-hbs="example">
  <p>\{{name}}</p>
</script>

2) 预编译

在将模板发送到客户端之前,这将在服务器上编译模板。这将使模板准备就绪,并减轻浏览器的负担。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接