• 让Mustache支持简单的IF语句


    转载:https://blog.csdn.net/iteye_16732/article/details/82070065

    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模板实现这个功能就需要在数据上下功夫,如下.

     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正确的渲染字符串.
    于是配合下面的模板就可以满足我们的要求

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

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

    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即可.
    模板变成了下面这样:

    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为自动生成):

     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.全部代码如下:

     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实现小巧却功能强大.

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

  • 相关阅读:
    转:基于科大讯飞语音API语音识别开发详解
    转:Netty系列之Netty高性能之道
    转:hadoop知识整理
    转:nginx防DDOS攻击的简单配置
    转:Google论文之一----Bigtable学习翻译
    POJ 2112 Optimal Milking(最大流+二分)
    HDU 4647 Another Graph Game(贪心)
    HDU 4671 Partition(定理题)
    HDU 4648 Magic Pen 6
    HDU 4649 Professor Tian(DP)
  • 原文地址:https://www.cnblogs.com/wjlwo2ni/p/10536689.html
Copyright © 2020-2023  润新知