1

我正在尝试创建一个函数,该函数采用模板名称并能够将呈现的模板作为字符串返回。我正在使用linkedin的灰尘版本。我正在将模板(使用grunt-dustjs任务)预编译成一个文件,如下所示:

(function(){dust.register("collections-nav",body_0);function body_0(chk,ctx){return chk.write("\t<div id=\"collection-loop\"><div class=\"section-title lines desktop-12\"><h2>Shop by Collection</h2></div>").section(ctx.getPath(false, ["bigMutha","TopNavigation"]),ctx,{"block":body_1},{}).write("</div>");}function body_1(chk,ctx){return chk.write("<div class=\"collection-index desktop-3 tablet-2 mobile-3 first\" data-alpha=\"").reference(ctx.get(["Title"], false),ctx,"h").write("\">  <div class=\"collection-image\"><a href=\"").reference(ctx.get(["Url"], false),ctx,"h").write("\" title=\"").reference(ctx.get(["Title"], false),ctx,"h").write("\"><img src=\"//cdn.shopify.com/s/files/1/0352/5133/collections/d_cb_20140312_m_handpicked_grande.jpg?v=1394885208\" alt=\"").reference(ctx.get(["Title"], false),ctx,"h").write("\" /></a>     </div><div class=\"collection-info\"><a href=\"/collections/mens-designer-clothing\" title=\"Browse our ").reference(ctx.get(["Title"], false),ctx,"h").write(" collection\"><h3>").reference(ctx.get(["Title"], false),ctx,"h").write("</h3><p>16 items</p></a></div></div>");}return body_0;})()

(function(){dust.register("index",body_0);var blocks={"body":body_1};function body_0(chk,ctx){ctx=ctx.shiftBlocks(blocks);return chk.partial("layouts/mainfull",ctx,{});}function body_1(chk,ctx){ctx=ctx.shiftBlocks(blocks);return chk.write("<ul>").section(ctx.get(["TopNavigation"], false),ctx,{"block":body_2},{}).write("</ul>").section(ctx.get(["Products"], false),ctx,{"block":body_3},{});}function body_2(chk,ctx){ctx=ctx.shiftBlocks(blocks);return chk.write("<li>").reference(ctx.get(["Title"], false),ctx,"h").write("</li>");}function body_3(chk,ctx){ctx=ctx.shiftBlocks(blocks);return chk.reference(ctx.get(["Name"], false),ctx,"h");}return body_0;})()

(function(){dust.register("layouts.mainfull",body_0);function body_0(chk,ctx){return chk.write("<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>Dust.js Test Template</title></head><body>").block(ctx.getBlock("body"),ctx,{},{}).write("</body></html>");}return body_0;})()

我认为我的最终问题是,我如何从节点加载/使用这些模板(它们在它们的单个文件中)?还是我错误地编译它们?我应该将这些 IIFE 包装在 module.exports 中吗?我这样做了,但什么也没做。这就是我在 .js 文件头部需要模板文件的方式:

var dust = require('dustjs-linkedin');
require('dustjs-helpers');
require('templates/all.js');
var JSON = require('json3');

当我通过“var templates = require(...);”按原样加载模板文件时 直接调用或 require() 它我首先得到一个“未定义灰尘”错误,然后当我添加“vardust = require('dustjs-linkedin');”时 到模板文件我收到一个错误,指出对象没有写入方法。

Object function (){dust.register("index",body_0);var blocks={"body":body_1};function body_0(chk,ctx){ctx=ctx.shiftBlocks(blocks);return chk.partial("layouts/mainfull",ctx,{});}function body_1(chk,ctx){ctx=ctx.shiftBlocks(blocks);return chk.write("<ul>").section(ctx.get(["TopNavigation"], false),ctx,{"block":body_2},{}).write("</ul>").section(ctx.get(["Products"], false),ctx,{"block":body_3},{});}function body_2(chk,ctx){ctx=ctx.shiftBlocks(blocks);return chk.write("<li>").reference(ctx.get(["Title"], false),ctx,"h").write("</li>");}function body_3(chk,ctx){ctx=ctx.shiftBlocks(blocks);return chk.reference(ctx.get(["Name"], false),ctx,"h");}return body_0;} has no method 'write'

问题是,为什么它认为没有“写”方法?试图加载这个我做错了什么?理论上,每个已编译的模板都应该在加载文件并执行 IIFE 时将自己注册到灰尘缓存中,但它一直抛出“没有方法'write'”错误。即使我将这些模板直接复制/粘贴到我试图加载它们的 .js 文件中,它也会这样做。我应该用“module.exports”代码包装编译的模板文件吗?也许在函数内部?我不知道为什么这不起作用,甚至不知道如何正确编译/加载模板。任何帮助表示赞赏!谢谢!


编辑 原始模板的要点


编辑

这是下面对公认答案的一个很好的解释。然而,我仍然有一个问题,但它似乎是在没有正确理解调用 .render() 时灰尘的作用以及我必须在 Edge.js/.NET 中执行此操作的交集。

注意:编译后的模板,带有分号 ;),位于顶部需要灰尘库的文件中,否则只是前面提到的 IIFE。在我的 Mac 上,在 Node.js 中,以下工作:

var dust = require('dustjs-linkedin');
dust.helpers = require('dustjs-helpers');
require('./templates/all.js');

var myFunction = function(data) {
    console.log(dust.cache);
}

module.exports = function(data) {
    return myFunction(data);
}

我可以在缓存中看到模板。但是,如果我随后将“myFunction”更改为此它仍然会看到缓存但返回未定义:

var dust = require('dustjs-linkedin');
dust.helpers = require('dustjs-helpers');
require('./templates/all.js');

var myFunction = function(data) {
    console.log(dust.cache);
    return dust.render('index', data, function(err, out) {
        return out;
    }
}

module.exports = function(data) {
    return myFunction(data);
}

这是一个问题。当我在 .NET 上下文中使用 Edge.js 时引入的另一个问题是,相同的设置不会像在我的 Mac 上的直接 node.js 环境中那样将模板加载到缓存中。我可以很好地加载文件,我什至可以将它作为字符串输出,但是当我查看dust.cache(由于console.log 无法在.NET 上下文中工作而导致的PITA)时,它返回为空。正是这个问题导致我尝试将编译后的模板转储到一个数组中,然后遍历数组,在每个数组项上调用dust.loadSource,但这也不想工作。

我正在清理项目以在今天某个时候发布到 GitHub。

4

2 回答 2

2

回答 2014-8-21 编辑

现在您正在谈论异步和同步之间的区别。

Dust 异步渲染模板。事实上,这是灰尘相对于其他模板系统的主要好处之一。所以让我们来看看你的第二个代码块中发生了什么:

  1. 在某个地方,您require将该代码块作为一个模块。为简单起见,我们假设代码块位于名为/myFunction.js. 所以,在别的地方,你说:

    var myFunction = require('./myFunction');
    var output = myFunction({ my: 'Model' }); // output === undefined
    
  2. myFunction记录dust.cache并返回返回值dust.render

  3. dust.render接受回调并立即返回undefined(因此您会看到预期的行为)
  4. 灰尘做它做的事,当它完全呈现模板字符串时调用你提供的回调
  5. 你的回调返回out,但你没有调用你的回调——灰尘做了——所以你的返回值立即掉到地上

您想要做的是访问模板字符串灰尘返回到回调。做到这一点的唯一方法是保持对该回调的控制。

考虑以下:

// a little shortcut since `dustjs-helpers` requires and returns dust anyway
var dust = require('dustjs-helpers');
require('./templates/all.js');

// `myFunction` uses dust which is async, therefore it needs to be async (take a callback)
var myFunction = function(data, cb) {
    console.log(dust.cache);
    dust.render('index', data, cb);
}

module.exports = myFunction;

// ... and here's example usage ...

var myFunction = require('./myFunction);
myFunction({ my: 'Model' }, function (err, templateStr) {
    if (err) {
        // ... dust had a problem ...
    } else {
        // ... do something with `templateStr` like ...
        console.log(templateStr);
    }
});

关于第二个问题,我会等待回购。;)


编辑:猜猜它有助于阅读完整的问题。你试过这个,它没有工作。这是如何生成模板的问题。

双重编辑:已修复。在 IIFE 的末尾添加分号。=P

有几种方法可以解决这个问题。

首先,如果您可以使用可以利用预编译模板的视图引擎,那就去吧。您必须将每个文件放在自己的文件中,并且模板名称必须与文件路径匹配,但这肯定是最简单的。例如,adaro可以呈现预编译的模板。您可以使用以下方式注册它:

var dust = require('adaro');
app.engine('js', dust.js());
app.set('view engine', 'js');
app.set('views', __dirname + '/views');

接下来,如果您不或不能将这些模板分解为它们自己的文件或更改名称以反映文件路径,那么接下来最简单的事情就是利用这两个事实:1)节点缓存模块和 2)dustjs-linkedin返回单例. 这意味着如果您require('dustjs-linkedin')在一个文件中,您将在任何其他文件中获得与require('dustjs-linkedin')*. 值得一提的是,这有点骇人听闻。

所以这意味着如果你在任何时候dust.register,你都可以dust.render使用该模板。您必须绕过 express 的视图渲染才能使其正常工作,但这是可能的。我写了一个例子并把它扔到了github上,但它的短处是:

  1. 在渲染的模板文件中添加对灰尘的引用

    // /templates/combined.js
    var dust = require('dustjs-linkedin');
    // your templates below
    (function () {dust.register('myTemplate', /* ... */})();
    
  2. 将灰尘拉入您的路线处理程序并使用灰尘进行渲染而不是快速渲染

    // /routes/index.js
    var dust = require('dustjs-linkedin');
    module.exports = function (req, res, next) {
      // instead of res.render ...
      dust.render('myTemplate', {my: 'Model'}, function (err, compiled) {
        if (err) return next(err);
        res.send(compiled);
      });
    };
    

由于您没有使用 express 的便捷渲染方法,因此您可以使用灰尘的流接口将输出流式传输到客户端,而不是在内存中缓冲渲染的模板。事实上,流式传输是我考虑使用这种模式的唯一原因,因为虽然这是一种功能性解决方法,但它有点不优雅,并且依赖于我个人建议不要依赖的东西(来自模块的单例) .

另一种选择是编写自己的视图引擎,而不是只在文件系统上查找模板,而是先检查灰尘缓存,然后公开一种setup允许您提前填充缓存的方法。

最后,如果您不喜欢这些解决方案中的任何一个,请查看krakenjs(完全披露:我正在研究这个)。它以及它支持的模块类型(如kraken-devtools)无需考虑很多这些东西。使用yeoman 生成器轻松尝试。

* - 节点缓存模块文件路径,因此仅当您的 require 语句解析为相同的文件路径时才适用。换句话说,子依赖的dustjs-linkedin 将不同于您的dustjs-linkedin 依赖。

于 2014-08-20T22:11:10.210 回答
0

所以我将回答我自己的问题,这将适用于我的具体问题,但我不会像 Jean-Charles 那样尝试就加载 Dust.js 模板的一般问题提供彻底的答案。他将保留已接受的答案,而我的答案可能是对尝试手动呈现预编译模板的人的脚注警告。特别是模板预编译成单个文件。也可能是 Adaro 可以进行的切换,希望通过解析多个文件来提高效率

最终我的问题是单个文件“templates.js”以错误的顺序加载已编译的模板。所以 index.js,它使用了一个布局(实际上只是一个花哨的部分),但是还没有为索引模板加载布局模板来尝试用作布局。

我的解决方案是改变我的 grunt-dustjs 任务:

[{ 
    "templates/all.js": ["theme/**/*.dust"] 
}]

对此:

[{
"templates/all.js":    [
                        "theme/partials/[singletons]/*.dust",
                        "theme/partials/*.dust",
                        "theme/layouts/*.dust",
                        "theme/*.dust"
                       ]
}]

这在目录树上向后走,强制执行模板只能使用部分的结构,该部分是对等或后代。这本质上是对编译器说“首先给我我的个人(重用)部分,然后是我的组件级部分,然后是我的布局(这将是部分的主要消费者,以及顶级灰尘文件),然后是我的可以被认为是页面的顶级灰尘文件”。页面将是布局的消费者和服务器端模板请求的入口点。

令人不安的部分是 [singletons] 区域。不可避免地,一个partial 将需要它的peer,并且该peer 将在字母表中的所述partial 之前使用一个字母命名。这将导致我在加载布局模板之前遇到需要布局模板的索引模板时遇到的相同情况。除非我遗漏了什么,否则这将默默地失败。它将成功地将每个模板加载到缓存中,但是需要尚未加载的模板的模板将编译错误并且在调用时无法呈现。

于 2014-08-23T18:16:16.623 回答