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

Node.js with Handlebars.js on server and client

本文关键字:js Handlebars 客户端 服务器 Node      更新时间:2023-09-26

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

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

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

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

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

将用index.hbs.的内容替换{{{body}}}标签

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

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

谢谢!

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

  1. 全球安装把手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://stackoverflow.com/a/13884587/8360

一种简单的方法是在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>

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

是的,这是一个棘手的问题——有点像shell脚本中的引用问题,它变成了引用的老鼠窝。

我的解决方案是在expressjs(服务器端)中使用jade(a la haml)为客户端输出基于手柄的模板。通过这种方式,服务器使用一种语法(jade),客户端使用另一种语法。我和你处于同一个十字路口,所以我面临着同样的挑战。

当然,玉不是必不可少的(尽管它是为expressjs准备的)。你可以为服务器选择任何(非手柄)模板引擎,和/或你可以在服务器上使用手柄和在客户端上使用非手柄模板——只要你选择的模板引擎的两种语法不冲突。由于我在客户端上使用emberjs,并且它使用了handlebas语法(默认情况下),所以我更喜欢在客户端上用emberjs+handlebas句法。因此expressjs+jade成为服务器的天然选择。

无耻的自我推销

我想做同样的客户端/服务器共享,所以我写了一个小的npm包来帮助:

节点把手预编译器

我在几个小时内根据wycats的handlebas repo中的命令行编译器快速完成了它。这不是世界上最伟大的代码,但它很好地完成了我的工作。

编辑:我不再维护此程序包。如果你想接任,请通过Github与我联系。我现在主要使用Jade模板,所以继续作为维护人员是没有意义的

我通过将客户端模板传递到服务器端模板来解决这个问题。

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

在您的路线处理程序中执行以下操作:

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

然后在布局中。bbs:

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

在这里,我使用没有扩展名的文件名作为模板id,这样它们就可以从主干视图中引用。哦,记住为生产模式实现缓存。

是的,这太糟糕了。

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

我不喜欢预编译解决方案(因为我想在使用模板的同一文件中定义模板),也不喜欢简单的'{{转义解决方案(由于它需要完整的Handlebars编译器和更多的javascript代码),所以我想出了一个使用Handlebars助手的混合解决方案:

1) 在服务器配置上注册一个名为"模板"的新助手

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) 逃离胡子

<script type="text/x-handlebars" data-hbs="example">
  <p>'{{name}}</p>
</script>

2)预编译

这将在模板到达客户端之前在服务器上编译模板。这将使模板可以随时使用,并减轻浏览器的负担。