一、前言

说起前端模板引擎,那可真是多如牛毛,只要是前端coder,怎么着你都听说或用过几款,社区里面的文章也有介绍,或者问问度娘,这里不再赘述。其中比较知名的有 artTemplate、doT、mustache等。

本文介绍两款极简模板引擎:一款原创format引擎,一款优化template引擎。每个模板引擎都只有区区三十行左右的代码。

二、模板原则

模板概念的提出,不管起源是什么,最根本的原则一定是要解决开发中的问题:显示逻辑和数据逻辑能够分离。而实际开发需求中,两者分离的需求也不尽相同。

三、实际问题

在实际开发中,我们很多时候就是以下几种模板格式化需求:

1、将一堆数据直接塞到模板字符串中,源数据很多的时候是对象,保不齐也需要是数组或者多参;

2、偶尔用一下模板中的循环逻辑、判断逻辑;

3、如果太复杂,就干脆load一段json然后配合一个函数来处理了,复杂度和可维护性往往是冲突的;

四、战术匕首

先看第一个需求:直接将数据塞入到字符串中。

如果撇开前端模板不谈,你会用什么方法?很显然,在 js 中处理字符串最为得心应手的莫过于正则表达式,一个简单的 replace 函数就可以完成查询和替换的工作:

1 var str = "<div>我是一个{name}</div>";
2 var data = {name:"测试用例"};
3 var ret = str.replace(/\{(\w+)\}/g, function(match, index) {
4     return data[index] || match;
5 });
6 console.log(ret)

上面这段代码足够简单,但是思路却是非常赞,使用js内置的正则引擎来高效处理字符串替换。而这个工作其实就是“模板引擎”的本职工作。

考虑数据源多样性、正则表达式预编译等需求,最终完善成以下本文要介绍的原创format引擎:

(function($, undefined) {//测试浏览器是否支持正则表达式预编译var baseReg = /\{([\w\.]+)\}/g, numReg = /^\d+$/,//预编译核心的正则表达式,以提高正则匹配效率formatReg = baseReg.compile ? baseReg.compile(baseReg.source, "g") || baseReg : baseReg,//其他工具函数toString = Object.prototype.toString, slice = Array.prototype.slice;//对外接口$.format = function(string, source){if( source === undefined || source === null )return string;var isArray = true, type = toString.call(source),//检测数据源data = type === "[object Object]" ? (isArray = false, source) : type === "[object Array]" ? source : slice.call(arguments, 1),N = isArray ? data.length : 0;//执行替换return String(string).replace(formatReg, function(match, index) {var isNumber = numReg.test(index), n, fnPath, val;if( isNumber && isArray ){n = parseInt(index, 10);return n < N ? data[n] : match;}else{ //数据源为对象,则遍历逐级查找数据fnPath = index.split(".");val = data;for(var i=0; i<fnPath.length; i++)val = val[fnPath[i]];return val === undefined ? match : val;}});};
})(window.jQuery || window.Zepto || window);

如果拿一个武器来比喻的话,我觉得战术匕首很适合:短小精悍,使用顺手。

这把刀是这样用的:

//模板内置到js中
var tmpl = ['<div>','这里是一个{name}','这里是其他元素',
'</div>'].join("");
var ret = $.format(tmpl, data);//从页面元素中获取模板
var tmpl = $("#tmpl")[0].innerHTML;
var ret = $.format(tmpl, data);

以上引擎能解决实际开发中多数的模板需求了,但是在有些时候,数据结构比较复杂,有循环、判断等逻辑在内,这个场景下,format就力不从心了。

五、沙漠之鹰

jQuery的作者John Resig的一个Micro-Templating非常合我胃口,不足三十行的代码,完成了上述含有逻辑处理的模板引擎,如果说format是一把前端开发中的战术匕首的话,这个无疑是一只沙漠之鹰。不过,这只沙漠之鹰用起来不太顺手:

1、只有从页面读取的模板才能缓存,而且必须是id元素;

2、为了节约前缀数据名,使用with这个令我觉得不爽的语法;

3、识别标识符不能更换,因为有些模板会输出到 jsp 页面(请谅解我们项目的特殊性),<%%>跟jsp的标志混淆;

于是,动手优化了大神的代码,经过实践完善后的代码如下(中间完善的过程不再一一叙述):

(function($) {var tmplCache={}, fnCache={}, guid=0, toString = Object.prototype.toString, compile = function( tmpl, sp ){//默认分隔符var f = sp || "%",//动态创建函数,并增加数据源引用(data/my)fn = new Function("var p=[],my=this,data=my,print=function(){p.push.apply(p,arguments);};p.push('" +// Convert the template into pure JavaScript
                tmpl.replace(/[\r\t\n]/g, " ").split("<" + f).join("\t").replace(new RegExp("((^|" + f + ">)[^\\t]*)'", "g"), "$1\r").replace(new RegExp("\\t=(.*?)" + f + ">", "g"), "',$1,'").split("\t").join("');").split(f + ">").join("p.push('").split("\r").join("\\'") + "');return p.join('');");return fn;};//对外接口$.template = function(tmpl, data, sp) {sp = sp||"%";var fn = toString.call(tmpl) === "[object Function]" ? tmpl: !/\W/.test(tmpl) ? fnCache[tmpl+sp] = fnCache[tmpl+sp] || compile(document.getElementById(tmpl).innerHTML, sp): (function(){for(var id in tmplCache)if( tmplCache[id] === tmpl ) return fnCache[id];return (tmplCache[++guid] = tmpl, fnCache[guid] = compile(tmpl, sp));})();return data ? fn.call(data) : fn;};
})(window.jQuery || window.Zepto || window);

优化后的代码仅仅保留了最为核心的正则处理部分,同时增加以下特性:

1、任意模板均自动编译和缓存;

2、模板可以从js输入,也可以从页面id元素中获取;

3、支持自定义分隔符;

4、支持数据源别名;

因为最终模板会编译成一个函数,所以,js能干的事儿,模板中都可以干,同时语法上跟 jsp 的标志相似,有过类似开发经验的同学几乎没有任何学习成本,是的,这个引擎也仅仅三十行左右。

经过优化改造的引擎,趁手了,强大了,用起来才有沙漠之鹰的感觉:

这把枪的用法如下:

//模板内置到js中
var templ = "this is a <%=my.type%> <%=data.name%> too.";
var ret = $.template(templ, {name:"demo", type:"easy"});//从页面id元素中获取模板
var ret = $.template("tmpl", {name:"demo", type:"easy"});

更多用法,请移步这里。

六、语法和性能

模板引擎几乎都会自造模板语法,但是就开发习惯而言,当你看到以下模板的时候,不知道作为前端开发人员,你会不会糊涂:

<ul>{{ for (var val, i = 0, l = it.list.length; i < l; i ++) { }}{{ val = it.list; }}<li>用户: {{=val[i].user}}/ 网站:{{=val[i].site}}</li>
    {{ } }}
</ul>

所以,在性能和语法习惯上,我个人非常倾向于后者。而性能问题,在日常开发中,不会变态到循环 10000 次,每次有 100 条数据的情况吧?

这个引擎在性能上不占太多优势(IE下除外),但是极为精简的代码,可以让你感觉不到他的存在,完全可以放入全站js中,随时供您调遣和使用。

当然,如果你的需求远不止上述两个方面,你可以尝试使用其他模板引擎,当然,还是建议优先考虑开发习惯,然后参考性能、体积等因素综合考虑,其中 doT 是除了语法习惯有点别扭之外,性能和体积综合最为优秀的一款引擎。而企鹅公司的artTemplate在支持模板调试,并可以在node环境下使用,功能还是很强大的。

我用artTemplate的性能测试用例跑了一下,本文从这里开始到末尾均是性能测试截图。为了防止名称跟其他模板冲突,测试用例中,沙漠之鹰的名字换成了 mcTemplate。有兴趣的同学,可以到这里自己试验:

上图为 chrome下高频测试结果

上图为 chrome下低频测试结果

上图为 firefox下高频测试结果

上图为 firefox下低频测试结果

上图为 IE11 下高频测试结果

上图为 IE11 下低频测试结果

IE7 下无法执行高频测试,直接卡死。上图是IE7低频测试结果

IE6 下无法执行高频测试,直接卡死。上图是IE6低频测试结果

转载于:https://www.cnblogs.com/zjcn/p/4275816.html

沙漠之鹰和战术匕首--两款前端极简模板引擎相关推荐

  1. 小巧 linux 网页浏览器,冷门软件!几款干净极简浏览器,小巧强大、高效出众...

    原标题:冷门软件!几款干净极简浏览器,小巧强大.高效出众 1.夸克浏览器 夸克浏览器,UC出品的一款干净极简的手机浏览器. 没有新闻推送.没有天气挂件.没有游戏插件.没有购物频道.没有精准广告.只做最 ...

  2. vue+elementUI 后台管理极简模板

    vue+elementUI 后台管理极简模板 写在前面 此篇文章为一篇说明文档,不是教你从零构建一个后台管理系统,而是基于一个实际项目,已经搭建好了一个后台管理系统的基础框架,教你如何在此基础上快速开 ...

  3. 【JavsScript】推荐五款流行的JavaScript模板引擎

    摘要:Javascript模板引擎作为数据与界面分离工作中最重要一环,受到开发者广泛关注.本文通过开发实例解析五款流行模板引擎:Mustache.Underscore Templates.Embedd ...

  4. 常见php模板_几款常见的PHP模板引擎

    PHP是一种HTML内嵌式的在服务器端执行的脚本语言,所以大部分PHP开发出来的Web应用,初始的开发模板就是混合层的数据编程.虽然通过MVC设计模式可以把程序应用逻辑与网页呈现逻辑强制性分离,但也只 ...

  5. 全新二开PHP自适应极简多引擎搜索单页网站源码

    介绍: 一个极简的搜索页面,同时兼顾了网址导航功能. 本项目由https://github.com/616620131/Simple-Search-Page 二次开发,加入了全网资源聚合搜索框,并且包 ...

  6. micropython ota_物联网产品的首选方案——5款追求极简设计的ESP32-PICO-D4开发板

    在物联网大行其道的今天,谈到乐鑫的ESP32系列产品真可谓无人不知无人不晓,但说真的,其中最让我钟情的还是那款基于ESP32的变种,采用SiP封装.简单到极致的ESP32-PICO-D4,原因无它,主 ...

  7. Windows server远程桌面配置【只需两步的极简快速版】

    1.右键我的电脑,点击远程设置,按以下图片设置 2.关闭防火墙 3.登录 OK

  8. easyexcel根据模板写入_用 Vue+ElementUI 搭建后台管理极简模板

    本文由图雀社区成员 灿若星空[1] 写作而成,欢迎加入图雀社区,一起创作精彩的免费技术教程,予力编程行业发展. 写在前面 此篇文章为一篇说明文档,不是教你从零构建一个后台管理系统,而是基于一个实际项目 ...

  9. element ui 前台模板_用 Vue+ElementUI 搭建后台管理极简模板

    本文由图雀社区成员https://blog.csdn.net/crxk_​blog.csdn.net 写在前面 此篇文章为一篇说明文档,不是教你从零构建一个后台管理系统,而是基于一个实际项目,已经搭建 ...

最新文章

  1. Dropout, DropConnect ——一个对输出,一个对输入
  2. [BZOJ4556][Tjoi2016Heoi2016]字符串 主席树+二分+倍增+后缀自动机
  3. android 通知显示时间,android:在特定时间显示通知?
  4. 活动分享|今晚启明云端与您相约立创直播间,红包\彩屏开发板送不停!
  5. 2017-9-15-Linux移植:WinSCP软件 SSH Server开启
  6. query row php,php – 如何在Codeigniter上使用$query- row获取类对象
  7. 如何获取web视频数据流的传输?小姐姐的视频都被我爬下来了,这谁顶得住
  8. linux刷windows phone,老机焕新生!Lumia 950XL也能跑Win10
  9. 计算机网络 自顶向下方法
  10. love2d 1. 入门
  11. 寻找软件智能的根--- aaas内部遵循的唯一原则
  12. java实现lbs_如何在 Java 中利用 redis 实现 LBS 服务
  13. 信息流广告如何操作?一文搞懂!
  14. Linux IPTABLES 防火墙专题讲座 - 下篇-龙小威-专题视频课程
  15. 简述在android中如何发送广播消息,Android Intent发送广播消息实例详解
  16. 网络存储技术Windows server 2012(项目三 存储池的配置与管理)
  17. Android 长度单位(dp、sp、px、in、pt、mm)详解
  18. ASEMI高压MOS管ASE65R330参数,ASE65R330图片
  19. 数据库设计之冗余字段
  20. 如何安装群晖系统,改成NAS?

热门文章

  1. 纯CSS实现酷炫渐变色、旋转动画特效
  2. 射击小游戏c语言实验报告,C++实现简单射击小游戏
  3. FloatingPointError: Predicted boxes or scores contain Inf/Nan. Training has diverged.
  4. 有限责任公司的股东会详解
  5. 图像美化笔记:DSLR-Quality Photos on Mobile Devices with Deep Convolutional Networks
  6. 2023全国两会政府工作报告中的“数据安全”
  7. AI+金融:学者、产业、趋势全景报告
  8. windows 系统查看端口号并杀死
  9. layui-dtree 重设根节点parentId的值
  10. 网店日常经营 那些必须的步骤