自定义标签可以有内容,也可以嵌套使用。本文描述了如何实现一个由开始标签,结束标签和之间的标签内容的自定义标签。
渲染标签内容
我们先实现一个简单的 wrap 标签,它会把内容包装在 <div class="wrapper"></div> 元素里:
1 | {% wrap %} |
期望输出:
1 | <div class='wrapper'> |
首先 注册 一个名为 wrap 的标签,把内容解析到 this.tpls 数组里。parse(tagToken, remainTokens) 中,
tagToken是当前 Token{% wrap %},remainTokens是当前模板中后续所有 Token 的数组。
我们要做的是从 remainTokens 里拿出来/.shift() 足够的标签直到遇到 endwrap(其实可以是任意名字,但按照惯例应该叫 endwrap)。如果到模板结尾都没遇到 endwrap,需要抛出一个标签未关闭的 Error。
1 | engine.registerTag('wrap', { |
.renderTemplates() 可能是异步的,因此需要 yield 来等它完成。更多关于 LiquidJS 异步的信息可以参考 同步和异步。render() 的其他部分比较直观,这是 JSFiddle 版本:http://jsfiddle.net/por0zcn1/3/。
使用 ParseStream
对于像 for 和 if 这样的复杂标签,parse() 会变得很复杂。使用 ParseStream 工具可以按事件风格来组织 parse() 的逻辑。下面是用 ParseStream 重写过的 parse(),实现了和上面例子中完全一样的功能。
1 | parse(tagToken, remainTokens) { |
这是 JSFiddle 链接:http://jsfiddle.net/por0zcn1/4/。简单起见,下面的例子都借助 ParseStream 来实现。
操作上下文
上面的 wrap 标签看起来没什么用,反正没它也可以很容易地渲染那部分内容。我们现在来实现一个 repeat 标签,把内容渲染两次(还可以加个参数让它渲染任意次):
1 | {% repeat %} |
期望输出:
1 | 1. Hello world! |
你可能注意到了在 repeat 上下文里有个额外的变量 repeat.i,这就需要我们操作 上下文。
上下文上下文 定义了 Liquid 模板中每个变量的值。在 LiquidJS 中,
Context由一个Scope的栈组成。Scope 就是一个普通对象,就像传给engine.render(tpl, scope)的scope一样。
每次进入新的 上下文 时,我们需要 push 一个新的 Scope。当结束渲染并退出 上下文 时,再把 Scope 从 上下文 pop 出来。见下面的实现:
1 | engine.registerTag('repeat', { |
parse() 部分和 wrap 标签完全相同,在 render() 部分我们通过调用两次 .renderTemplates(this.tpls) 来重复渲染内容。这是 JSFiddle 链接:http://jsfiddle.net/por0zcn1/2/。
成对使用 Push 和 Pop必须成对地使用
context.push()和context.pop()。如果忘记pop()会导致Scope泄露给后面的模板内容,也可能损坏 上下文 栈。.