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

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

让Mustache支持简单的IF语句的更多相关文章

  1. 一条简单的更新语句,MySQL是如何加锁的?

    看如下一条sql语句: # table T (id )) delete : MySQL在执行的过程中,是如何加锁呢? 在看下面这条语句: : 那这条语句呢?其实这其中包含太多知识点了.要回答这两个问题 ...

  2. 『片段』OracleHelper (支持 多条SQL语句)

    C# 调用 Oracle 是如此尴尬 >System.Data.OracleClient.dll —— .Net 自带的 已经 过时作废. >要链接 Oracle 服务器,必须在 本机安装 ...

  3. Memcache仅仅支持简单数据类型

    Memcache仅仅支持简单数据类型 ,复杂数据类型需要应用自己处理 从数据库当中取出数据[User [id=1, username=guowuxin, password=guowuxin], Use ...

  4. Tornado 模板支持“控制语句”和“表达语句”的表现形式

    Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {% 和 %} 包起来的 例如 {% if len(items) > 2 %}.表达语句是使用 {{ 和 }} 包起来的,例 ...

  5. [20190328]简单探究sql语句相关mutexes.txt

    [20190328]简单探究sql语句相关mutexes.txt --//摘要:http://www.askmaclean.com/archives/understanding-oracle-mute ...

  6. sql最简单的查询语句

    -- 2 **************************************************** -- 最简单的查询语句 -- 2.1 ----------------------- ...

  7. ThinkPHP框架 系统规定的方法查询数据库内容!!同时也支持原生的SQL语句!

    <?php namespace Admin\Controller; use Think\Controller; class MainController extends Controller{ ...

  8. 四种简单的sql语句(增删改查语句)

    四种简单的sql语句(增删改查语句) 一.插入语句 insert into [table] ([column],[column],[column]) values(?,?,?) 二.删除语句 dele ...

  9. tp5 r3 一个简单的SQL语句调试实例

    tp5 r3 一个简单的SQL语句调试实例先看效果核心代码 public function index() { if (IS_AJAX && session("uid&quo ...

随机推荐

  1. PHP冒泡排序-手写

    <?php $a = [1,3,5,2,9,6]; for ($i = 0 ;$i < count($a) ;$i++) { for ($j = $i + 1;$j < count( ...

  2. LRU的实现

    https://blog.csdn.net/elricboa/article/details/78847305 未看懂https://zhuanlan.zhihu.com/p/34133067

  3. 谈谈如何给下拉框option添加点击事件?

    我们在用到下拉列表框select时,需要对选中的<option>选项触发事件,其实<option>本身没有触发事件方法,我们只有在select里的onchange方法里触发. ...

  4. java对redis的基本操作,ZZ

    java对redis的基本操作 http://www.cnblogs.com/edisonfeng/p/3571870.html

  5. php逐行读取.txt文件内容,并解析每行内容

    // 读取nlp text 并存到mongodb public function readNLP(&$errorCode,&$errorMessage) { try{ // $_SER ...

  6. js 数组的pop(),push(),shift(),unshift()方法小结

    关于数组的一些操作方法小结: pop(),push(),shift(),unshift()四个方法都可改变数组的内容以及长度: 1.pop() :删除数组的最后一个元素,并返回被删除的这个元素的值: ...

  7. python 实现树结构

    简述: 研究  MCTS 过程中, 需要用到树结构.  baidu  google 了一番, 找不到自己能满足自己的库或代码参考,只好再造个轮子出来 我造的树用来下五子棋 和 围棋用的,   有其它不 ...

  8. CAD小小调整,复制生成二层5.28

    1.栏杆剖切索引:“符号标注”“索引符号",填写文字,标注效果: 2,台阶剖切索引:填写文字,标注效果: 3.符号标注:图名标注: 4一层平面图完成.复制生成二层平面,把图名改为”二层平面图 ...

  9. javascript的event loop事件循环

    javascript的event loop事件循环 这是今天一个朋友发给我的一个面试题, 感觉还挺有意思的, 写个博客以供分享 先看看这个面试题目: 观察下面的代码,写出输出结果 console.lo ...

  10. 关于规范NOIP试题管理办法的通知

    由CCF主办的NOIP赛事举行在即,保密起见,现将有关规定发给各省赛区组织单位. 1.NOI各省组织单位负责试题保密工作. 2.NOIP初赛试卷为纸质版,复赛试卷为电子版. 3.在初赛进行中,如有选手 ...