`
limu
  • 浏览: 323088 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

让Mustache支持简单的IF语句

阅读更多
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模板实现这个功能就需要在数据上下功夫,如下.
data = {
	list:[
		{ id:"1",status"P"},
		{ id:"2",status"W"},
		{ id:"3",status"R"}
	],
	statusRenderer:function(){
		if(this.status=="P"){
			return '<b style="color:green">通过</b>'
		}else if(this.status=="W"){
			return '等待'
		}else{
			return '<b style="color:red">拒绝</b>'
		}
	}
}

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

项目是很复杂的,如果需要写无数statusRenderer那会非常累,所以我想Renderer功能强大,在遇到各种特殊情况时,单独写一下未尝不可,但是想上面这种常见需求是需要抽象一下的.
比如我们希望如下这样的模板,完成同样的需求.
<ul>
{{#list}}
{{#if(status==P)}}<li>ID:{{id}},status:<b style='color:green'>通过</b></li>{{/endif}}
{{#if(status==W)}}<li>ID:{{id}},status:等待</li>{{/endif}}
{{#if(status==R)}}<li>ID:{{id}},status:<b style='color:red'>拒绝</b></li>{{/endif}}
{{/list}}
</ul>

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

而输入给to_html方法的数据则变成如下这样(里边的Render为自动生成):
data = {
	list:[
		{ id:"1",status"P"},
		{ id:"2",status"W"},
		{ id:"3",status"R"}
	],
	//下面Renderer为自动生成.
	"if(status==P)":function(){
		if(this.status=="P"){
			return true;
		}
		return false;
	},
	"if(status==W)":function(){
		if(this.status=="W"){
			return true;
		}
		return false;
	},
	"if(status==R)":function(){
		if(this.status=="R"){
			return true;
		}
		return false;
	}
}

整个改造的大体流程如下,首先从模板中取出if(x.y.z==abc)这样的key,然后自动生成以"if(x.y.z==abc)"为名字的Renderer.全部代码如下:
    function addFns(template, data){
        var ifs = getConditions(template);
        var key = "";
        for (var i = 0; i < ifs.length; i++) {
            key = "if(" + ifs[i] + ")";
            if (data[key]) {
                continue;
            }
            else {
                data[key] = buildFn(ifs[i]);
            }
        }
    }
    function getConditions(template){
        var ifregexp_ig = /\{{2,3}[\^#]?if\((.*?)\)\}{2,3}?/ig;
        var ifregexp_i = /\{{2,3}[\^#]?if\((.*?)\)\}{2,3}?/i;
        var gx = template.match(ifregexp_ig);
        var ret = [];
        if (gx) {
            for (var i = 0; i < gx.length; i++) {
                ret.push(gx[i].match(ifregexp_i)[1]);
            }
        }
        return ret;
    }
    function buildFn(key){
        key = key.split("==");
        var res = function(){
            var ns = key[0].split("."), value = key[1];
            var curData = this;
            for (var i = ns.length - 1; i > -1; i--) {
                var cns = ns.slice(i);
                var d = curData;
                try {
                    for (var j = 0; j < cns.length - 1; j++) {
                        d = d[cns[j]];
                    }
                    if (cns[cns.length - 1] in d) {
                        if (d[cns[cns.length - 1]].toString() === value) {
                            return true;
                        }
                        else {
                            return false;
                        }
                    }
                } 
                catch (err) {
                }
            }
            return false;
        };
        return res;
    }
    // new to_html for exports
    function to_html(template, data){
        addFns(template, data);
        return Mustache.to_html.apply(this, arguments);    
    }

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

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

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

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

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

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

分享到:
评论
3 楼 lgoodbook 2015-07-14  
没有Logic-less模板,要如何实现?
    或者说 这个Logic-less模板文件和Mustache如何联系的?
小白,才接触Mustache,请教了!
2 楼 limu 2011-05-30  
lifesinger 写道
木头,看看这个:
http://writing.jan.io/mustache-2.0.html

在 mustache 2 出来前,可以尝试:

https://github.com/wycats/handlebars.js

做了很多扩展,社区也非常活跃,推荐。


收到 我看下 谢谢玉伯.
1 楼 lifesinger 2011-05-30  
木头,看看这个:
http://writing.jan.io/mustache-2.0.html

在 mustache 2 出来前,可以尝试:

https://github.com/wycats/handlebars.js

做了很多扩展,社区也非常活跃,推荐。

相关推荐

    Mustache: PHP实现的Mustache模板语言类库.zip

    此外,Mustache支持部分、循环和条件语句,这些都是通过特定的标签实现的。 在PHP中,使用Mustache需要引入对应的类库。在这个案例中,`mustache.php-master`可能包含了实现这些功能的PHP类和文件。通常,会有一个...

    Laravel开发-mustache-l4

    它支持部分(Partials)、循环(Loops)和条件语句(Conditions),但不包含任何内建的控制结构,如if/else或for/while,这使得模板保持简洁。 **2. Mustache-L4 的安装与配置** 在 Laravel 4 项目中使用 Mustache-...

    简单了解vue 插值表达式Mustache

    在Vue中使用插值表达式Mustache非常简单。你可以直接在模板中使用双大括号将要显示的变量包起来,如{{ variableName }},这里的variableName是你在Vue实例的data属性中定义的数据对象的属性名。例如,如果你在Vue...

    yii2-mustache:Yii 框架的 Mustache 模板

    Mustache遵循一种称为"logic-less templates"的原则,这意味着它不包含任何内建的控制结构,如if语句或循环。模板由变量、部分和三元运算符组成,它们通过简单的标签语法来定义。这种设计使得模板与业务逻辑分离,更...

    硬皮:{{Mustache}} for Crystal

    Crustache支持多种控制结构,如条件语句(`{{#if}}`、`{{else}}`)和循环(`{{#each}}`)。这些结构使得模板可以依据数据动态地改变输出。例如: ```mustache {{#if user}} &lt;p&gt;Welcome, {{user.name}}! {{else}} ...

    晶须:R的{{mustache}}

    此外,Whisker还支持条件语句(如{{#if}}{{/if}})和循环(如{{#each}}{{/each}}),使得模板可以处理更复杂的逻辑。 Whisker与Mustache的主要区别在于,Whisker针对R做了优化,更好地融入了R的生态系统。例如,它...

    Javascript最简单的模板引擎

    例如,Mustache遵循“逻辑less”的原则,它的语法更简单,不支持条件语句和循环,而是通过预先计算好的数据来决定输出什么。Handlebars则在Mustache的基础上增加了助手函数和块助手,增强了模板的表达能力。 在...

    前端开源库-blackbaud-stache

    - **指令系统**:提供了丰富的内置指令,如 `if`、`unless`、`each`,以及自定义指令能力,增强了模板的表达力。 - **延迟渲染**:能延迟渲染未准备好的数据,提高用户体验。 - **部分模板**:允许定义和重用部分...

    简单的前端模板引擎能实现最基本的数据渲染

    在实际应用中,前端模板引擎通常会更复杂,包括支持条件语句、循环、函数调用等功能。例如,Mustache和Handlebars是两个流行的JavaScript模板引擎,它们提供了丰富的语法结构,如`{{#if}}...{{/if}}`(条件判断)和`...

    javascropt模板

    条件语句和循环结构则有各自的语法结构,如`{{#if condition}}...{{/if}}`和`{{#each array}}...{{/each}}`。 4. **实际应用** - **动态数据绑定**:在用户界面中,模板可以实时更新数据显示,如表单验证时的错误...

    OBLOG 如果

    在OBLOG中,可能有一个内置的模板引擎,允许用户使用特定的语法(如Mustache、Twig或EJS)来编写条件语句。 3. **变量和表达式**:在模板中,我们可以使用变量来存储和展示数据。例如,`{{ article.title }}` 可能...

    vue3源码解析 vue3源码解析 vue3源码解析 vue3源码解析

    此外,Vue3还支持条件语句(v-if/v-else)、循环(v-for)、事件处理(v-on)等高级特性,使得模板更加灵活和强大。 3. 数据响应式原理: Vue3的响应式系统基于新的ES Proxy对象实现,可以监听对象的属性访问和...

    lilship

    例如,如果一个变量不存在,Mustache 将简单地跳过该部分,而不是抛出错误。 3. **部分(Partial)**:部分允许你将重复的模板代码封装起来,然后在多个地方复用。这对于保持代码整洁和可维护性至关重要。 4. **三...

    javascript-高性能javascript模版引擎-templateEngine.zip

    2. **条件语句**:如`if`、`else`,允许根据数据状态展示不同的内容。 3. **循环结构**:如`for`,用于遍历数组或对象,生成重复的HTML结构。 4. **函数调用**:支持在模板中嵌入自定义函数,增强模板的灵活性。 5. ...

    handlebars官方例子及源代码

    3. 条件语句:Handlebars支持`if`和`unless`语句。例如,`{{#if isTrue}}...{{else}}...{{/if}}`用来判断条件是否为真。 4. 每个循环:`{{#each items}}...{{/each}}`用于遍历数组或对象的每个元素。 5. 助手函数:...

    jquery 模板

    3. **条件语句**:`&lt;#if condition#&gt;...&lt;#/if#&gt;` - 根据条件决定是否渲染某部分模板。 4. **循环语句**:`&lt;#each item in array#&gt;...&lt;#/each#&gt;` - 遍历数组,为每个元素渲染模板。 5. **逻辑运算**:在`{{...}}`内...

    ctemplate c++渲染库

    1. **模板语法**:ctemplate支持简单的模板语言,允许开发者在模板中插入变量、控制结构(如条件语句和循环)以及函数调用。变量通常以`$var_name$`的形式表示,而控制结构则通过特定的语法实现,如`&lt;% IF condition...

    spring boot 模板

    5. **条件和循环**:Thymeleaf提供了类似编程语言中的条件语句(if、else)和循环结构(foreach),可以在模板中处理复杂逻辑。 6. **表达式语言**:Thymeleaf有自己的表达式语言(THYMELEAF Expression Language, ...

    开源项目-captncraig-temple.zip

    4. **条件和循环**:了解模板引擎如何支持条件语句(如 if/else)和循环结构(如 for 或 each),以实现复杂的逻辑控制。 5. **库的集成**:知道如何将 captncraig-temple 库集成到现有的项目中,可能涉及引入依赖...

    模版引擎 例子

    - **条件语句**:支持if/else、三元运算等,根据数据情况决定是否渲染某部分模板。 - **循环结构**:用于遍历数组或对象,如`#each`或`#foreach`。 - **函数调用**:在模板中可以调用预定义的函数,处理数据。 - ...

Global site tag (gtag.js) - Google Analytics