【js模板引擎】萌新也能看懂的模板引擎

2024-03-05 08:59

本文主要是介绍【js模板引擎】萌新也能看懂的模板引擎,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

从一个实际需求出发认识模板引擎
实现一个模板引擎的思路及步骤

从一个实际需求出发认识模板引擎

当我们需要用js渲染一个歌曲列表的时候,由于数据需要向后端请求,所以实现不能在html中把数据写死。
如下所示,我们需要在页面上展示这样一个列表,

展示列表

但是却不能像下面一样把数据写死在li标签中。
这里写图片描述

这个时候有两种我们容易思考得到的方法来解决这个问题,一个是html字符串拼接,另一种是构建DOM对象。
第一种:HTML字符串拼接
这里写图片描述
第二种:构建DOM对象
这里写图片描述

但是这两种方法都有很大缺陷:
第一种方法容易漏引号,出现拼接错误。
第二种方法则是代码量增加,虽然可以用jquery来减少代码量,但依然不是最优选择。
这两种方法的共同特征都是需要创建一个li标签,然后里面的内容动态获取

var li =  '<li>'+ songs[i].name +'-'+songs[i].singer'</li>'

在这里我们可以使用一种更好的办法,

var li = stringFormat('<li>{0} - {1}</li>', songs[0].name, songs[0].singer)

构建一个stringFormat函数,向里面传入参数,第一参数是模板,{0},{1}会被后面传入的两个参数替换,这样当函数执行后返回的就是一个字符串

"<li>Some Like It Hot!!  - SPYAIR</li>"

这个stringFormat函数可以说,就是一个最为粗略的模板引擎了。
传入的参数:

'<li>{0} - {1}</li>'

就是一个模板,所谓的模板引擎就是能够解析含有特殊字符的逻辑代码,简化字符串拼接前端渲染小插件。

但是这样的话,就必须把这部分函数写在script标签中,否则无法区分哪部分是html代码,哪部分是逻辑代码,所以我们需要特殊的字符来表示,这个特殊的字符就是定界符,
比如这个模板,使用的定界符就是<%%>:

'<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>';

实现一个模板引擎的思路及步骤:

以上举例的模板还并不算复杂,当我们遇到下面这个模板的时候,情况就会变得不同。

var template = 
'My skills:' + 
'<%if(this.showSkills) {%>' +'<%for(var index in this.skills) {%>' + '<a href="#"><%this.skills[index]%></a>' +'<%}%>' +
'<%} else {%>' +'<p>none</p>' +
'<%}%>';
var data = {skills: ["js", "html", "css"],showSkills: true
}
var TemplateEngine = function(template, data) {return string
} 

现在需要理清楚我们的思路,我们拿到一个模板,需要让它里面有定界符包裹的代码能够像js代码一样运行,最后返回给我们一个包含了运行结果的字符串,我们现在需要的就是实现
1. 创建一个新数组,将没有定界符包裹的代码部分放入数组中
2. 分辨出js逻辑部分,也就是有定界符包裹的部分,取出,
3. 传入data
这样最后返回给我们的就是这样一个字符串,

这里写图片描述
最终代码如下:

var TemplateEngine = function(html, options) {var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[];\n', cursor = 0;var add = function(line, js) {js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :(code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');return add;}while(match = re.exec(html)) {add(html.slice(cursor, match.index))(match[1], true);cursor = match.index + match[0].length;}add(html.substr(cursor, html.length - cursor));code += 'return r.join("");';return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);
}

可能很多人看不懂这个最终版本,下面我将把这个复杂版本简单化,添加注释,以便于理解:

var TemplateEngine = function(html, options) {//正则匹配出<%%>包裹的逻辑代码部分var re = /<%([^%>]+)?%>/g;//正则匹配出含有if|for|else|switch|case|break|{|}等关键字var reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g;//声明一个字符串code,用来保存我们最终输出的结果var code = 'var r=[];\n';//设置一个游标,用来遍历完我们传入的整个模板字符串var cursor = 0;//声明一个add函数,用来判断是js脚本部分,还是需要添加到数组中的字符串部分var add = function(line, js) {if(js === true){if(line.match(reExp)){code = code +   line + '\n'}else{code = code +  'r.push(' + line + ');\n'}}else{if(line !== ''){code = code + 'r.push("' + line.replace(/"/g, '\\"') + '");\n'; //引号添加转义符}else{code = code + ''}}return add}while( match = re.exec(html) ) {var matchIdx = match.index;//使用re正则匹配,在模板字符串中匹配到的第一个部分的最后一个字符的下一个字符的下标var matchPart = html.slice(cursor,matchIdx);//从模板字符串中截取出下标从cursor,到matchIdx的部分add(matchPart);//只传了第一参数,lineadd(match[1], true); //第二个参数设为true的话,直接进入if(js===true)这个判断中cursor = match.index + match[0].length; //游标更新}var rest = html.substr(cursor, html.length - cursor);//匹配结束后,模板字符串中最后剩余的部分add(rest);//这一部分也需要进入add函数中判断code = code +  'return r.join("");';//最后添加返回语句code = code.replace(/[\r\t\n]/g, '');//去除code字符串中的制表符,回车符和换行符等,使代码看起来更加简洁return new Function(code).apply(options); //将code放入Function()函数中运行,并且将code中的变量作用域绑定到data上。
}

最终我们会获得这样一个字符串code,
这里写图片描述
利用JS给我们提供了构造函数的“类”,
这里写图片描述
可以运行code
注意
这里写图片描述
这里之所以会报错,是因为console.log中添加的双引号没有加上转义符(\),所以会报错

完结:
写完这篇博客断断续续差不多用了两天的时间,理解一个东西不难,但是怎么用文字说明白就很伤脑筋了,不过也算是前进了一小步啦。
参考博客

这篇关于【js模板引擎】萌新也能看懂的模板引擎的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/775965

相关文章

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

uva 1342 欧拉定理(计算几何模板)

题意: 给几个点,把这几个点用直线连起来,求这些直线把平面分成了几个。 解析: 欧拉定理: 顶点数 + 面数 - 边数= 2。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

uva 11178 计算集合模板题

题意: 求三角形行三个角三等分点射线交出的内三角形坐标。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

最大流、 最小费用最大流终极版模板

最大流  const int inf = 1000000000 ;const int maxn = 20000 , maxm = 500000 ;struct Edge{int v , f ,next ;Edge(){}Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){}};int sourse , mee

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件