本文总结它的使用方法和一些使用心得,内容不算很高深,纯粹是入门内容,看看即可。不过要是你还没有用过此类的javascript引擎库,那么本文还是值得你一读的,相信在你了解完它强大的功能和简单用法之后,一定会迫不及待地将之用于你的工作当中。
1. 从一个简单真实的需求讲起
目前公司做了一个统一的开发平台,后台封装了MVC的接口和数据增删改查的接口,前端我自己用bootstrap+手写各类组件的方式弄了一套开发框架;集成了CAS,在CAS的基础上,首先做了一套统一权限管理系统,这个系统是我们开发平台的第一个子系统,用来管理配置所有子系统的菜单和授权以及管理整个公司的组织结构和用户,后来我们又陆陆续续地开发了业务系统A和业务系统B。由于这三个子系统对应的是三个java工程,最终部署的时候,在tomcat里部署了三个应用,现在有一个需求是:
1)在每个系统里登录之后,点击系统名称,可以展开一个下拉菜单,显示所有有权限的子系统;
2)然后用户点击其它子系统,就可以切换到所选中的系统去,到了其它系统之后,由于都做了这个下拉菜单,所以也可以再从该系统切换回来;
3)如果用户只有一个系统的权限,则不显示下拉菜单。
需求其实挺简单,原型大概是这个样子:
功能实现方法是,在每个子系统登录完成之后,调用获取系统列表的接口,用js渲染一个下拉菜单出来,该接口返回的格式为:
data: [ { "sortOrder": 1, "isCurrent": true, "systemHttpUrl": "http://xxxx:8080/permission", "systemName": "统一权限管理系统" }, { "sortOrder": 2, "isCurrent": false, "systemHttpUrl": "http://xxxx:8080/systemA", "systemName": "业务系统A" }, { "sortOrder": 3, "isCurrent": false, "systemHttpUrl": "http://xxxx:8080/systemB", "systemName": "业务系统B" } ]
如果我们不采用模板引擎,那么传统的方式去解析这个数据并把它转变成html串的方法通常是:
function data2Html(data) { data = data || []; var html = ['<ul>', ' <li>', ' <a href="javascript:;" data-toggle="dropdown" title="切换系统">'], l = data.length; if(l < 2) { l == 1 && html.push(data[0].systemName || ''); html.push('</a></li></ul>'); return html.join(''); } var curSysAry = data.filter(function(s){ return s.isCurrent; }); html.push(curSysAry[0].systemName + ' <i></i></a><ul>'); data.sort(function(a, b){ return a.sortOrder - b.sortOrder;}); for(var i = 0; i < l; i++) { i && html.push('<li role="separator"></li>'); html.push('<li><a href="' + data[i].systemHttpUrl + '" target="_self">' + data[i].systemName + '</a></li>'); } html.push('</ul></li></ul>'); return html.join(''); }
这种拼接字符串的方式有诸多弊端:
1)麻烦,尤其是拼接逻辑复杂,拼接的串很长时;
2)不易维护,稍有不慎就会弄错标签的对应关系;
3)结构不清晰。
能够简化这个场景的工具就是模板引擎,模板引擎的技术后台最先有,如果你用过jsp,就一定知道jsp也就是一个模板,用来解析呈现数据用的,其它后台模板引擎还有velocity和freemarker等等。前端的模板引擎也有很多,mustache.js算是比较流行的一个,git上有8000多个赞,如果这个问题我们用mustache.js来做,就可以变成这样:
//通过一些根据属性名称对应的标记定义模板 var _template = [ '<ul>', ' <li>', ' <a href="javascript:;" data-toggle="dropdown" title="切换系统">', ' {{curSystemName}} {{#multiple}}<i></i>{{/multiple}}', ' </a>', ' {{#multiple}}<ul>', ' {{#systems}}', ' {{^first}}<li role="separator"></li>{{/first}}', ' <li>', ' <a href="https://www.jb51.net/{{{systemHttpUrl}}}" target="_self">{{systemName}}</a>', ' </li>', ' {{/systems}}', ' </ul>{{/multiple}}', ' </li>', '</ul>' ].join(''); //初始化这个模板 Mustache.parse(_template); function data2Html(data) { data = data || []; var curSysAry = data.filter(function(s){ return s.isCurrent; }); data.sort(function(a, b){ return a.sortOrder - b.sortOrder;}); data = data.map(function(s, i){s.first = i == 0; return s}); //模板渲染成字符串 return Mustache.render(_template, { curSystemName: curSysAry.length ? curSysAry[0].systemName : '', multiple: !!data.length, systems: data }); }
对比两个代码,会发现后面的代码,相对于前面的有以下这些优点:
1)结构清晰,所有待渲染的html都定义在一个位置,而且没有任何拼接的现象;
2)逻辑清晰,那些在模板里的标记,实际上与模板渲染时传进去的对象的属性名称都是对应的;
3)易维护,要增删标签都只用调整模板对应的数组就行了。