文档章节

让Mustache支持简单的IF语句

fz00x0zf
 fz00x0zf
发布于 2016/10/11 01:54
字数 1245
阅读 12
收藏 0

Mustache是一种Logic-less templates.不支持if这类条件判断是Logic-less的显著特征之一.Mustache的另一个特征是体积小,不依赖其他前端类库,在浏览器端和NodeJS中都可以运行. 

并非Logic-less.Mustache的体积小,无依赖,前后兼容才是我们当前的项目选择这套模板系统的真正原因.没有IF有时候感觉并不给力,所以就想办法简单扩展下Mustache,让其具有一些通用的条件判断能力. 

比如如下的应用场景,我们需要根据某一字段的值,决定输出有意义的中文,并用颜色加以修饰. 

status=="P"  ==>  <b style="color:green">通过</b> 
status=="W"  ==>  等待 
status=="R"  ==>  <b style="color:red">拒绝</b> 

Logic-less模板实现这个功能就需要在数据上下功夫,如下. 

Javascript代码 

 收藏代码

  1. data = {  
  2.     list:[  
  3.         { id:"1",status"P"},  
  4.         { id:"2",status"W"},  
  5.         { id:"3",status"R"}  
  6.     ],  
  7.     statusRenderer:function(){  
  8.         if(this.status=="P"){  
  9.             return '<b style="color:green">通过</b>'  
  10.         }else if(this.status=="W"){  
  11.             return '等待'  
  12.         }else{  
  13.             return '<b style="color:red">拒绝</b>'  
  14.         }  
  15.     }  
  16. }  


这里的statusRenderer就是在数据这边扩展做的工作,{{{statusRenderer}}}在渲染时,this指向当前context,在取得status之后,经过判断,return正确的渲染字符串. 
于是配合下面的模板就可以满足我们的要求 

Html代码 

 收藏代码

  1. <ul>  
  2. {{#list}}  
  3. <li>ID:{{id}},status:{{{statusRenderer}}}</li>  
  4. {{/list}}  
  5. </ul>  


项目是很复杂的,如果需要写无数statusRenderer那会非常累,所以我想Renderer功能强大,在遇到各种特殊情况时,单独写一下未尝不可,但是想上面这种常见需求是需要抽象一下的. 
比如我们希望如下这样的模板,完成同样的需求. 

Html代码 

 收藏代码

  1. <ul>  
  2. {{#list}}  
  3. {{#if(status==P)}}<li>ID:{{id}},status:<b style='color:green'>通过</b></li>{{/endif}}  
  4. {{#if(status==W)}}<li>ID:{{id}},status:等待</li>{{/endif}}  
  5. {{#if(status==R)}}<li>ID:{{id}},status:<b style='color:red'>拒绝</b></li>{{/endif}}  
  6. {{/list}}  
  7. </ul>  


这个改造看起来一定是会伤筋动骨的,因为完全打破了{{#xxx}}{{/xxx}}这种Mustache的嵌套模式.改过之后Mustache就不再是Mustache了. 
于是我们在这里妥协下,把{{/endif}}改为{{/if(status==W)}},这样{{#if(status==W)}}{{/if(status==W)}}就配起对来了. 
接下来我们就只要想办法从模板中把这类标签正则出来,然后为这类配对自动添加Renderer即可. 
模板变成了下面这样: 

Html代码 

 收藏代码

  1. <ul>  
  2. {{#list}}  
  3. {{#if(status==P)}}<li>ID:{{id}},status:<b style='color:green'>通过</b></li>{{/if(status==P)}}  
  4. {{#if(status==W)}}<li>ID:{{id}},status:等待</li>{{/if(status==W)}}  
  5. {{#if(status==R)}}<li>ID:{{id}},status:<b style='color:red'>拒绝</b></li>{{/if(status==R)}}  
  6. {{/list}}  
  7. </ul>  


而输入给to_html方法的数据则变成如下这样(里边的Render为自动生成): 

Javascript代码 

 收藏代码

  1. data = {  
  2.     list:[  
  3.         { id:"1",status"P"},  
  4.         { id:"2",status"W"},  
  5.         { id:"3",status"R"}  
  6.     ],  
  7.     //下面Renderer为自动生成.  
  8.     "if(status==P)":function(){  
  9.         if(this.status=="P"){  
  10.             return true;  
  11.         }  
  12.         return false;  
  13.     },  
  14.     "if(status==W)":function(){  
  15.         if(this.status=="W"){  
  16.             return true;  
  17.         }  
  18.         return false;  
  19.     },  
  20.     "if(status==R)":function(){  
  21.         if(this.status=="R"){  
  22.             return true;  
  23.         }  
  24.         return false;  
  25.     }  
  26. }  


整个改造的大体流程如下,首先从模板中取出if(x.y.z==abc)这样的key,然后自动生成以"if(x.y.z==abc)"为名字的Renderer.全部代码如下: 

Javascript代码 

 收藏代码

  1. function addFns(template, data){  
  2.     var ifs = getConditions(template);  
  3.     var key = "";  
  4.     for (var i = 0; i < ifs.length; i++) {  
  5.         key = "if(" + ifs[i] + ")";  
  6.         if (data[key]) {  
  7.             continue;  
  8.         }  
  9.         else {  
  10.             data[key] = buildFn(ifs[i]);  
  11.         }  
  12.     }  
  13. }  
  14. function getConditions(template){  
  15.     var ifregexp_ig = /\{{2,3}[\^#]?if\((.*?)\)\}{2,3}?/ig;  
  16.     var ifregexp_i = /\{{2,3}[\^#]?if\((.*?)\)\}{2,3}?/i;  
  17.     var gx = template.match(ifregexp_ig);  
  18.     var ret = [];  
  19.     if (gx) {  
  20.         for (var i = 0; i < gx.length; i++) {  
  21.             ret.push(gx[i].match(ifregexp_i)[1]);  
  22.         }  
  23.     }  
  24.     return ret;  
  25. }  
  26. function buildFn(key){  
  27.     key = key.split("==");  
  28.     var res = function(){  
  29.         var ns = key[0].split("."), value = key[1];  
  30.         var curData = this;  
  31.         for (var i = ns.length - 1; i > -1; i--) {  
  32.             var cns = ns.slice(i);  
  33.             var d = curData;  
  34.             try {  
  35.                 for (var j = 0; j < cns.length - 1; j++) {  
  36.                     d = d[cns[j]];  
  37.                 }  
  38.                 if (cns[cns.length - 1] in d) {  
  39.                     if (d[cns[cns.length - 1]].toString() === value) {  
  40.                         return true;  
  41.                     }  
  42.                     else {  
  43.                         return false;  
  44.                     }  
  45.                 }  
  46.             }   
  47.             catch (err) {  
  48.             }  
  49.         }  
  50.         return false;  
  51.     };  
  52.     return res;  
  53. }  
  54. // new to_html for exports  
  55. function to_html(template, data){  
  56.     addFns(template, data);  
  57.     return Mustache.to_html.apply(this, arguments);      
  58. }  


看起来这样做的好处是保持了Mustache的配对风格,并且继续无缝支持原生的嵌套以及否定等语法. 
但看起来模板挺丑的,性能损耗也一定是有不少的. 

后续的扩展,我想elseif肯定不能支持了,if中带"与""或"判断倒是还方便添加的. 
当然还可以做很多扩展,比如给数组增加一些内置属性如"_index_", "_first_", "_last_", "_odd_", "_even_". 

Mustache仍然足够简单,它本身就具有循环和否定判断等特性,增加了IF后,稍微加了点逻辑,但能少写很多Renderer. 

重要的是我们依然保有我们所看重的东西:体积小,无依赖,前后兼容. 
在我的项目里破坏了Logic-less是我的事情并不接受批判,但请大家根据自己实际情况谨慎选择. 

刚刚接触Mustache,各种特性还在学习摸索中,现在看起来Lambda和子模板等特性,让Mustache的JS实现小巧却功能强大. 

或许今天的需求还有更好的解决方案,如果有同学知道还望不吝赐教.

本文转载自:http://limu.iteye.com/blog/1064024

fz00x0zf
粉丝 2
博文 79
码字总数 7180
作品 0
广州
程序员
私信 提问
字符串模板浅析

前言 虽然现在有各种前端框架来提高开发效率,但是在某些情况下,原生 JavaScript 实现的组件也是不可或缺的。例如在我们的项目中,需要给业务方提供一个通用的支付组件,但是业务方使用的技...

滴滴WebApp架构组
2017/12/18
0
0
mustache.js

mustache.js 是一个 Mustache 模板系统的 JavaScript 实现。 Mustache 模板语法的逻辑比较简单。它用于HTML,配置文件,源代码等。它的工作方式是通过通过以哈希值或者对象的方式扩展模板标签...

匿名
2012/03/16
10.3K
0
javascript 模板引擎--artTemplate

artTemplate 是新一代 javascript 模板引擎,它采用预编译方式让性能有了质的飞跃,并且充分利用 javascript 引擎特性,使得其性能无论在前端还是后端都有极其出色的表现。在 chrome 下渲染效...

匿名
2012/06/15
34.7K
2
高性能JavaScript模板引擎template.js原理解析

artTemplate是新一代javascript模板引擎,它在v8中的渲染效率可接近javascript性能极限,在chrome下渲染效率测试中分别是知名引擎Mustache与micro tmpl的25/32倍(性能测试);artTemplate的模板还...

webmirror
2017/11/29
0
0
Node.js 使用 Mustache.js 做模板

为了让 Node.js 输出更多的 HTML 元素,我们可以借助一些 html 模板引擎,例如 Mustache。 首先在 Node.js 中安装 Mustache: npm install mustache 这会创建一个目录:node_modules\mustac...

红薯
2011/06/14
6.2K
8

没有更多内容

加载失败,请刷新页面

加载更多

好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
今天
6
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
今天
6
0
【技术分享】TestFlight测试的流程文档

上架基本需求资料 1、苹果开发者账号(如还没账号先申请-苹果开发者账号申请教程) 2、开发好的APP 通过本篇教程,可以学习到ios证书申请和打包ipa上传到appstoreconnect.apple.com进行TestF...

qtb999
今天
10
0
再见 Spring Boot 1.X,Spring Boot 2.X 走向舞台中心

2019年8月6日,Spring 官方在其博客宣布,Spring Boot 1.x 停止维护,Spring Boot 1.x 生命周期正式结束。 其实早在2018年7月30号,Spring 官方就已经在博客进行过预告,Spring Boot 1.X 将维...

Java技术剑
今天
18
0
浅谈java过滤器Filter

一、简介 Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断如是否有权限访问页面等。其工作原理是,只要你在web.xml...

青衣霓裳
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部