Custom Tags

你可以添加更多的自定义扩展,nunjucks 提供了 parser api 可以对模板做更多的事。

注意:当预编译时,你必须在编译时添加这些扩展,你应该使用 precompiling API (或者 grunt gulp任务),而不是预编译脚本。你需要创建一个 Environment 实例,添加扩展,传到预编译器中。

一个扩展至少有两个字段 tagsparse,扩展注册一个标签名,如果运行到这个标签则调用 parse。

tags 为这个扩展支持的一组标签名。parse 为一个函数,当编译时会解析模板。除此之外,还有一个特殊的节点名为 CallExtension,在运行时你可以调用本扩展的其他方法,下面会详细说明。

因为你需要直接使用 parse api,并且需要手动分析初 AST,所以有一点麻烦。如果你希望做一些复杂的扩展这是必须的。所以介绍一下你会用到的方法:

  • parseSignature([throwErrors], [noParens]) - 解析标签的参数。默认情况下,解析器会从括号左边解析到括号右边。但是自定义标签不应该时括号,所以将第二个参数设为 true 告诉解析器解析参数直到标签关闭。参数之间应该用逗号分隔,如 {%
    mytag foo, bar, baz=10 %}

  • parseUntilBlocks(names) - 解析内容直到下一个名为 names 的标签,非常有用解析标签之间的内容。

parser API 还需要更多的文档,但现在对照上面的文档和下面的例子,你还可以看下源码

最常用使用的是在运行时解析标签间的内容,就像过滤器一样,但是更灵活,因为不只是局限在一个表达式中。通常情况下你会解析模板,然后将内容传入回调。你可以使用 CallExtension,需要传扩展的实例,方法名,解析的参数和内容(使用 parseUntilBlocks 解析的)。

例如,下面实现了从远程获取内容并插入的扩展:

  1. function RemoteExtension() {
  2. this.tags = ['remote'];
  3. this.parse = function(parser, nodes, lexer) {
  4. // get the tag token
  5. var tok = parser.nextToken();
  6. // parse the args and move after the block end. passing true
  7. // as the second arg is required if there are no parentheses
  8. var args = parser.parseSignature(null, true);
  9. parser.advanceAfterBlockEnd(tok.value);
  10. // parse the body and possibly the error block, which is optional
  11. var body = parser.parseUntilBlocks('error', 'endtruncate');
  12. var errorBody = null;
  13. if(parser.skipSymbol('error')) {
  14. parser.skip(lexer.TOKEN_BLOCK_END);
  15. errorBody = parser.parseUntilBlocks('endremote');
  16. }
  17. parser.advanceAfterBlockEnd();
  18. // See above for notes about CallExtension
  19. return new nodes.CallExtension(this, 'run', args, [body, errorBody]);
  20. };
  21. this.run = function(context, url, body, errorBody) {
  22. var id = 'el' + Math.floor(Math.random() * 10000);
  23. var ret = new nunjucks.runtime.SafeString('<div id="' + id + '">' + body() + '</div>');
  24. var ajax = new XMLHttpRequest();
  25. ajax.onreadystatechange = function() {
  26. if(ajax.readyState == 4) {
  27. if(ajax.status == 200) {
  28. document.getElementById(id).innerHTML = ajax.responseText;
  29. }
  30. else {
  31. document.getElementById(id).innerHTML = errorBody();
  32. }
  33. }
  34. };
  35. ajax.open('GET', url, true);
  36. ajax.send();
  37. return ret;
  38. };
  39. }
  40. env.addExtension('RemoteExtension', new RemoteExtension());

模板可以这么写:

  1. {% remote "/stuff" %}
  2. This content will be replaced with the content from /stuff
  3. {% error %}
  4. There was an error fetching /stuff
  5. {% endremote %}

Asynchronous

如果是异步的可以使用 CallExtensionAsync,在运行时扩展有一个回调作为最后一个参数,模板渲染会等待回调返回。

上面例子中的 run 如下使用

  1. this.run = function(context, url, body, errorBody, callback) {
  2. // do async stuff and then call callback(err, res)
  3. };

如果你做了些有趣的东西的话,请记得把他们添加到wiki中!

原文: https://mozilla.github.io/nunjucks/cn/api.html