html模板引擎的作用,如何用javascript实现一个模板引擎
模板引擎简介
模板引擎是html渲染必不可少的工具,前端开发的同学经历了从最原始的字符串拼接、用数组push/join,发展到封装简单的string.format函数,再到功能更加强大的模板引擎,可以在模板中内嵌处理逻辑。
传统的页面开发语言技术asp.net,php,jsp都内置了模板引擎,javascrip常见的模板引擎有jquery的tmpl插件,underscore的template函数,ejs、jade、artTemplate等,以ejs为例,模板语法如下:
复制代码
模板中可以直接访问绑定的数据源变量,并且可以支持任意的javascript代码片段,如if,for循环等
原理
我们从需求出发,想一想把上面的模板字符串中的变量替换成数据源中的变量值,并支持嵌入的if等逻辑,需要怎样实现?能支持内嵌js代码执行,那肯定是要使用eval来编译的,只要把模板看成两部分,一部分是在百分号以内的,使用eval执行;另一部分是在百分号以外的,原样输出; 最后顺序拼接成一个完整的字符串就可以了。
正则表达式分割字符串
首先要把模板,按照%分隔进行拆分成多个代码片段,并按顺序存入到数组中
分割模板字符串可以用String对象的split方法,很多人不知道split方法是可以支持正则表达式的,因为我们的分隔符有两个,所以要用正则表达式,可以同时匹配这两个字符,代码就只有一行:
var arr=str.split(/(\)/gm);
复制代码
拆分后的代码片段生成一个数组,我们遍历一下这个数组,将代码片段分成3种类型
javascript代码
html片段(原样输出的字符)
用输出的变量值
只要按顺序拼接生成的字符串代码,最终编译执行字符串代码就可以了
编译字符串的方法
1. eval
js最常见的编译字符串的方法当然是eval,可以直接在当前函数作用域将一个字符串编译成可执行代码,有一条代码规范叫做don't be eval,这是因为使用eval是用代码生成代码,会让代码更加难以看懂,并且会有一些性能损耗。
2. new Function
与eval功能相似,不同的是他会将传入的字符串代码编辑后生成一个function,而且可以生成函数的参数,更加方便调用,封装性隔离性更好。调用形式:new Function(args,functionBody),为了更好的可读性,我们选择new Function
3. setTimeout/setInterval
setTimeout/setInterval 的第一个参数,可以传入一个函数,这是最常规的用法,也可以传入一个字符串,使用字符串的时候自动调用eval执行,这个用法不太常见。
作用域问题
模板中肯定需要一些动态变量,就是所谓的数据源,模板编译后是生成独立函数的,作用域是隔离的,而数据源是在业务代码中,有独立的作用域,不能直接访问,那么怎么能把两个不同的作用域连接起来呢,with,只有with能做到,将指定的对象插入到当前作用域链的顶端。
完整代码实现
function render(tpl,data){
tpl=tpl.replace(/(\r|\n)/ig,"");
var arr=tpl.split(/(\)/gm);//拆分模板
var funcBody=["with(this){\r\nvar result=[];"];
var item,codeType;
codeType=0;
for(var i=0;i
item=arr[i];
//将代码片段分为3类
if(item=="
codeType=1;
continue;
}else if(item=="
codeType=2;
continue;
}else if(item=="%>"){
codeType=0;
continue;
}
//为3类代码片段生成最终可被eval的函数体
if(codeType==0){ //字符
funcBody.push("result.push(\"");
funcBody.push(item);
funcBody.push("\");\r\n");
}else if(codeType==1){ //代码
funcBody.push(item);
funcBody.push("\r\n");
}else if (codeType==2){ //代码输出
funcBody.push("result.push(");
funcBody.push(item);
funcBody.push(");\r\n");
}
}
funcBody.push("return result.join('')\r\n}");
var template_func=new Function(["renderData"],funcBody.join(""));
return template_func.apply(data,[data]);
}
复制代码
测试一下
var html=render(""
+"
+" ",
{list:["javascript","css","node.js"]})
document.write(html)
复制代码
完美运行
我们来看一下最终编译后生成的函数是什么样子的,如下图:
由于生成的函数是用apply调用的,template_inner.apply(data,[data]),所以函数内部的this指向传入的数据源(data变量),因此可以在模板中直接使用传入的数据源对象
模板引擎与ES6模板字符串对比
ES6新增了模板字符串功能,不同于普通字符串的单引号和双引号,模板字符用`符号定义,在模板字符中直接可以通过${变量名}访问当前作用域内的变量并直接输出该变量的值,并且在js文件中定义大段的html字符串时,一般是把html片段粘帖进来,包含很多换行符,而模板字符串可以直接兼容换行符,使用起来非常方便。上文中的模板,用ES6的模板字符串来实现,代码也非常精简,如下:
var user={name:"windy"};
var str=`
${user.name}
`
复制代码
ES6模板字符串与普通的模板引擎相比,最大区别在于开发流程的不同,业务逻辑是在js中实现的,模板只实现纯净的变量替换功能,代码与逻辑分离,比较规范易用,可维护也较好,而普通的模板引擎不仅实现了变量替换,还可以内嵌js逻辑代码,更加灵活和强大。
最后,推荐一下个人的开源项目,内置了模板引擎的node.js web开发框架,webcontext:
html模板引擎的作用,如何用javascript实现一个模板引擎相关推荐
- 说说如何用JavaScript实现一个模板引擎
前言 不知不觉就很长时间没造过什么轮子了,以前一直想自己实现一个模板引擎,只是没付诸于行动,最近终于在业余时间里抽了点时间写了一下.因为我们的项目大部分用的是 swig 或者 nunjucks ,于是 ...
- 如何用 javascript 做一个高逼格的进度条
可能你发现了本站顶部的进度条,它是如何实现的呢?下面一起来看. 页面进度条展示的是资源下载的进度,通常在页面上加上进度条,可以缓解用户的等待焦虑,也提升了网站的逼格. 前端进度条实现 在前端,实现网页 ...
- html渲染引擎开发,数据可视化初探-从零开始开发一个渲染引擎概述
项目地址在这里,欢迎fork和star 简介:在之前而有的生过脑单近需一相涯前网选近需一相涯的项目中用过一些图表,比如Echart.chart.js等.遂对其比较感兴趣,恰好最近在看canvas相关的 ...
- JavaScript中的模板(jsRender)在Django中的使用
有时,我们可能会想把存在js数组或字典里的数据放到html里面去,这时比较直接做法就是用js/jquery动态生成html代码,但数据量比较大或数据结构比较复杂时,我想javascript模板可能值得 ...
- 探索开发一个游戏引擎需要那些知识和技术
探索开发一个游戏引擎需要那些知识和技术 说到游戏引擎我们可能会想到Unity3D,虚幻引擎等.可是我们对于开发一个游戏引擎了解甚微,作为一名游戏开发者在开发游戏的同时我觉得也有必要了解并熟知如何开发一 ...
- 不用正则表达式,用javascript从零写一个模板引擎(一)
前言 模板引擎的作用就是将模板渲染成html,html = render(template,data),常见的js模板引擎有Pug,Nunjucks,Mustache等.网上一些制作模板引擎的文章大部 ...
- JavaScript中template模板引擎
这篇文章主要为大家详细介绍了JavaScript模板引擎Template.js使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 template.js 一款 JavaScript 模板引擎, ...
- [转]jquery的一个模板引擎-zt
jQuery-jTemplate.js下载:http://jtemplates.tpython.com/ 一 , 简单介绍 它是一个基于jQuery开发的javascript模板引擎.它主要的作用如 ...
- JavaScript设计前端模板初步理解
模板这个词,可能很多人第一印象是后端的技术(Smarty,Velocity等),但本文要讲的却不是后端的概念,而是前端开发中所使用到的一种技术,也就是"前端模板"技术. 模板的工作 ...
最新文章
- c语言贪心算法合并箭,LeetCode刷题题库:贪心算法
- 免费学python的网站-学数据分析Python必备的8个免费学习网站
- 【数字信号处理】序列傅里叶变换 ( 序列傅里叶变换与反变换 | 序列绝对可和 与 存在傅里叶变换之间的关系 | 序列傅里叶变换性质 )
- nginx反向代理和rewrite进行解决跨域问题、去掉url中的一部分字符串,通过nginx正则生成新的url...
- 看ExtJs API文档的阅读方法
- cv mat的shape_pybind11—opencv图像处理(numpy数据交换)
- StringUtils工具类说明
- 解读年度数据库性能:PostgreSQL的日志文件和数据加载
- STM32 IAP升级工具
- Java实验3 方法与数组
- python学习(1)启程
- 解决 Python 连不上pip库的问题(使用国内镜像地址)
- Go 标准库介绍五: io
- 大样本OLS模型假设及R实现
- OFD文件在线阅读器
- 天眼查是怎么获得企业工商信息的?
- 【STM32H750】玩转ART-Pi(八)——添加动态模块
- pta 机工士姆斯塔迪奥(C语言实现)
- windows通过资源管理器访问服务器(samba服务),您需要权限来执行此操作
- 明日方舟登录时服务器显示泰拉,明日方舟泰拉档案馆使用说明