Mustache中{{#items.0}}检查致上下文栈污染的问题及可行方案问询
根据Mustache官方文档及Perl、JS等主流实现,{{#items.0}}会将数组首项推入上下文栈是设计既定行为,但这确实是个容易踩坑的点——很多开发者会用这种写法判断数组是否有元素,再渲染容器和条目内容,但会导致后续数组项缺失某属性时,自动从首项的上下文取值,比如你示例里所有条目都被识别为first,就是因为该属性取自{{#items.0}}的上下文。
你的原模板代码:
{{#items.0}} <p>found items</p> {{#items}} {{#first}} <li><strong>{{name}}</strong></li> {{/first}} {{#link}} <li><a href="{{url}}">{{name}}</a></li> {{/link}} {{/items}} {{/items.0}} {{^items.0}} <p>no items</p> {{/items.0}}
错误输出中所有条目都渲染了first对应的strong标签,就是因为首项的first属性被后续条目继承了。
以下是几种无需将检查项加入上下文栈的解决方法:
1. 预处理数据添加存在性标记
在数据层面生成一个专门的布尔属性(比如hasItems),用来标识数组是否非空,这样判断时不会引入数组首项的上下文:
{{#hasItems}} <p>found items</p> {{#items}} {{#first}} <li><strong>{{name}}</strong></li> {{/first}} {{#link}} <li><a href="{{url}}">{{name}}</a></li> {{/link}} {{/items}} {{/hasItems}} {{^hasItems}} <p>no items</p> {{/hasItems}}
数据预处理时设置hasItems: items.length > 0即可,这种方法兼容性最好,几乎所有Mustache实现都支持。
2. 利用数组本身的真值判断+内置变量渲染头部
多数Mustache实现会将非空数组视为真值,因此可以直接用{{#items}}判断数组是否存在元素,再借助@first内置变量(如果实现支持)只在遍历第一个元素时渲染头部内容:
{{#items}} {{#@first}} <p>found items</p> {{/@first}} {{#first}} <li><strong>{{name}}</strong></li> {{/first}} {{#link}} <li><a href="{{url}}">{{name}}</a></li> {{/link}} {{/items}} {{^items}} <p>no items</p> {{/items}}
这种方法无需修改数据,直接通过模板逻辑实现,适合无法干预数据生成的场景。
3. 使用数组长度属性判断
如果你的Mustache实现支持将数组的length属性作为真值判断依据,可以直接用{{#items.length}}替代{{#items.0}}:
{{#items.length}} <p>found items</p> {{#items}} {{#first}} <li><strong>{{name}}</strong></li> {{/first}} {{#link}} <li><a href="{{url}}">{{name}}</a></li> {{/link}} {{/items}} {{/items.length}} {{^items.length}} <p>no items</p> {{/items.length}}
items.length是数字类型,非零时会被视为真值,判断时不会将数组首项推入上下文栈,后续渲染数组项时上下文是独立的。
你之前提到的将{{subitem.value}}改为{{#subitem}}{{value}}{{/subitem}}的方法虽然能规避部分场景,但仅适用于嵌套属性的情况,上面的方法更直接解决{{#items.0}}引入上下文栈的根本问题。
内容的提问来源于stack exchange,提问作者Deanna

