mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-16 21:00:40 -07:00
docs: update docs and demo for Value usage, fixes #568
This commit is contained in:
@@ -8,10 +8,10 @@ title: 注册标签和过滤器
|
||||
// 使用方式: {% upper name %}
|
||||
engine.registerTag('upper', {
|
||||
parse: function(tagToken, remainTokens) {
|
||||
this.str = tagToken.args; // name
|
||||
this.value = new Value(token.args, liquid)
|
||||
},
|
||||
render: async function(scope, hash) {
|
||||
var str = await this.liquid.evalValue(this.str, scope); // 'alice'
|
||||
render: function*(scope, hash) {
|
||||
const str = yield this.value.value(ctx); // 'alice'
|
||||
return str.toUpperCase() // 'Alice'
|
||||
}
|
||||
});
|
||||
@@ -20,7 +20,26 @@ engine.registerTag('upper', {
|
||||
* `parse`: 从 `remainTokens` 中读取后续的标签/输出/HTML,直到找到你期望的结束标签。
|
||||
* `render`: 把 scope 数据和此前解析得到的 Token 结合,输出 HTML 字符串。
|
||||
|
||||
查看已有的标签实现:<https://github.com/harttle/liquidjs/tree/master/src/builtin/tags>
|
||||
对于更复杂的标签实现,可以提供一个继承自 `Tag` 的类:
|
||||
|
||||
```typescript
|
||||
// Usage: {% upper name:"alice" %}
|
||||
import { Hash, Tag, TagToken, Context, Emitter, TopLevelToken, Liquid } from 'liquidjs'
|
||||
|
||||
engine.registerTag('upper', class UpperTag extends Tag {
|
||||
private hash: Hash
|
||||
constructor(tagToken: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
|
||||
super(tagToken, remainTokens, liquid)
|
||||
this.hash = new Hash(tagToken.args)
|
||||
}
|
||||
* render(ctx: Context) {
|
||||
const hash = yield this.hash.render();
|
||||
return hash.name.toUpperCase() // 'ALICE'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
可以参考已有的标签实现:<https://github.com/harttle/liquidjs/tree/master/src/tags>
|
||||
|
||||
## 注册过滤器
|
||||
|
||||
@@ -36,7 +55,7 @@ engine.registerFilter('upper', v => v.toUpperCase())
|
||||
engine.registerFilter('add', (initial, arg1, arg2) => initial + arg1 + arg2)
|
||||
```
|
||||
|
||||
查看已有的过滤器实现:<https://github.com/harttle/liquidjs/tree/master/src/builtin/filters>。对于复杂的标签,也可以用一个类来实现:
|
||||
查看已有的过滤器实现:<https://github.com/harttle/liquidjs/tree/master/src/filters>。对于复杂的标签,也可以用一个类来实现:
|
||||
|
||||
```typescript
|
||||
// Usage: {% upper name:"alice" %}
|
||||
|
||||
@@ -24,66 +24,89 @@ LiquidJS 支持同步调用也支持异步调用,支持 Promise。为了同异
|
||||
|
||||
## 如何实现兼容同步的标签
|
||||
|
||||
### 要求
|
||||
|
||||
所有内置标签都兼容同步,可以安全地用于同步或异步 API。为了让你的自定义标签页支持同步,你的标签不能返回 `Promise`,这意味着你的 `render(context, emitter)` 函数:
|
||||
|
||||
- 不能直接 `return <Promise>`,
|
||||
- 也不能声明为 `async`。
|
||||
|
||||
{% note info 不兼容同步的标签 %}
|
||||
不兼容同步的标签也仍然是合法标签,在异步 API 下也会正常运行。被同步调用时,返回 <code>Promise</code> 的标签会被渲染成 <code>[object Promise]</code>。
|
||||
{% endnote %}
|
||||
|
||||
### 等待 Promise
|
||||
但 LiquidJS 是支持 `Promise` 的,你仍然可以调用返回 `Promise` 的方法并等它 resolve。只需要把 `await` 换成 `yield` 并保留 `* render()` 不要改成 `async render()`。例如:
|
||||
LiquidJS 使用基于生成器的异步实现,来让同一份代码支持同步和异步调用。例如下面的 `UpperTag` 既可以用于 `engine.renderSync()` 也可以用于 `engine.render()`:
|
||||
|
||||
```typescript
|
||||
import { TagToken, Context, Emitter, TopLevelToken } from 'liquidjs'
|
||||
import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'
|
||||
|
||||
// Usage: {% upper "alice" %}
|
||||
// Output: ALICE
|
||||
engine.registerTag('upper', {
|
||||
parse: function(tagToken: TagToken, remainTokens: TopLevelToken[]) {
|
||||
this.str = tagToken.args
|
||||
},
|
||||
* render: function(ctx: Context) {
|
||||
// 同步调用时 `ctx.sync == true`,`_evalValue()` 会同步地执行
|
||||
var str = yield this.liquid._evalValue(this.str, ctx)
|
||||
return str.toUpperCase()
|
||||
}
|
||||
engine.registerTag('upper', class UpperTag extends Tag {
|
||||
private value: Value
|
||||
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
|
||||
super(token, remainTokens, liquid)
|
||||
this.value = new Value(token.args, liquid)
|
||||
}
|
||||
* render (ctx: Context, emitter: Emitter) {
|
||||
const title = yield this.value.value(ctx)
|
||||
emitter.write(title.toUpperCase())
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
见这个 JSFiddle:<http://jsfiddle.net/ctj364up/6/>。
|
||||
所有内置标签都兼容同步,可以安全地用于同步或异步 API。实现同时支持同异步的标签,需要:
|
||||
|
||||
## 只支持异步的标签
|
||||
- render 函数声明成 `* render()`,并且在里面
|
||||
- 不能直接 `return <Promise>`,
|
||||
- 不能调用会返回 Promise 的函数。
|
||||
|
||||
对于只用于异步 API 的标签,或者只能实现为异步的标签,使用生成器语法和 async 语法并没有区别。
|
||||
## 调用返回 Promise 的函数
|
||||
|
||||
例如,如果上面的 `this.liquid._evalValue()` 不会检查 `ctx.sync` 而且总是返回一个 `Promise`,那么即使这个标签用 `* render()` 和 `yield this.liquid._evalValue()` 实现,最终也会渲染成 `<object Promise>`。
|
||||
|
||||
这时可以直接使用 async 语法。注意有些 LiquidJS API 会返回 `Promise`,有些会返回生成器。你需要用 [toPromise][toPromise] API 来把生成器转换为 `Promise`,比如:
|
||||
但 LiquidJS 是支持 `Promise` 的,你仍然可以调用返回 `Promise` 的方法并等它 resolve。只需要把 `await` 换成 `yield`。例如:
|
||||
|
||||
```typescript
|
||||
import { TagToken, Context, Emitter, TopLevelToken, toPromise } from 'liquidjs'
|
||||
* render (ctx: Context, emitter: Emitter) {
|
||||
const file = yield this.value.value(ctx)
|
||||
const title = yield fs.readFile(file, 'utf8')
|
||||
emitter.write(title.toUpperCase())
|
||||
}
|
||||
```
|
||||
|
||||
现在 `* render()` 调用了一个返回 Promise 的 API,它就不再兼容同步了。不兼容同步的标签也仍然是合法标签,在异步 API 下也会正常运行。被同步调用时,返回 <code>Promise</code> 的标签会被渲染成 <code>[object Promise]</code>。
|
||||
|
||||
## 把 LiquidJS 生成器转换成 Promise
|
||||
|
||||
有些 LiquidJS API 会返回 `Promise`,有些会返回生成器。你可以用 [toPromise][toPromise] 来把生成器转换为 `Promise`,比如:
|
||||
|
||||
```typescript
|
||||
import { TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid, toPromise } from 'liquidjs'
|
||||
|
||||
// Usage: {% upper "alice" %}
|
||||
// Output: ALICE
|
||||
engine.registerTag('upper', {
|
||||
parse: function(tagToken: TagToken, remainTokens: TopLevelToken[]) {
|
||||
this.str = tagToken.args; // name
|
||||
},
|
||||
render: async function(ctx: Context) {
|
||||
var str = await toPromise(this.liquid._evalValue(this.str, ctx));
|
||||
// Or use the alternate API that returns a Promise
|
||||
// var str = await this.liquid.evalValue(this.str, ctx);
|
||||
return str.toUpperCase()
|
||||
}
|
||||
});
|
||||
engine.registerTag('upper', class UpperTag extends Tag {
|
||||
private value: Value
|
||||
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
|
||||
super(token, remainTokens, liquid)
|
||||
this.value = new Value(token.args, liquid)
|
||||
}
|
||||
async render (ctx: Context, emitter: Emitter) {
|
||||
const title = await toPromise(this.value.value(ctx))
|
||||
emitter.write(title.toUpperCase())
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
见这个 JSFiddle:<http://jsfiddle.net/ctj364up/5/>。
|
||||
## 纯异步标签
|
||||
|
||||
如果你的标签就不打算支持同步,可以干脆实现成 `async render()`,这样就可以使用更熟悉的 `await` 了:
|
||||
|
||||
```typescript
|
||||
import { toPromise, TagToken, Context, Emitter, TopLevelToken, Value, Tag, Liquid } from 'liquidjs'
|
||||
|
||||
// Usage: {% upper "alice" %}
|
||||
// Output: ALICE
|
||||
engine.registerTag('upper', class UpperTag extends Tag {
|
||||
private value: Value
|
||||
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
|
||||
super(token, remainTokens, liquid)
|
||||
this.value = new Value(token.args, liquid)
|
||||
}
|
||||
async render (ctx: Context, emitter: Emitter) {
|
||||
const title = await toPromise(this.value.value(ctx))
|
||||
emitter.write(`<h1>${title}</h1>`)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
[Liquid]: /api/classes/liquid_.liquid.html
|
||||
[toPromise]: /api/modules/liquid_.html#toPromise
|
||||
|
||||
Reference in New Issue
Block a user