您的位置:首页 > 其它

自己动手撸个模板引擎(50行左右)

2016-09-26 19:56 267 查看

写在前面

模板的诞生是为了将显示与数据分离,模板技术多种多样,但其本质是将模板文件和数据通过模板引擎生成最终的HTML代码。目前有着很多这种模板引擎,诸如Node的
ejs
jade
,PHP的
Smarty
。当然在用过这么多的模板引擎后,也有着自己实现一个简易模板引擎的冲动。于是今天就实现了一个简单的模板引擎,这个模板引擎非常简单,并不会涉及到语法分析,词法分析等编译原理相关知识,做的仅仅是将模板的
js
代码执行
。下面来和大家分享下:

实现模板引擎

语法参照的是
ejs


<% js code %>


<%= 变量名 %>


看下需要实现的目标:

// 模板代码
<ul>
<% for (var i = 0; i < 5; i++) { %>
<% if (i === 0) { %>
<li><%= i %></li>
<% } else { %>
<li><%= (i + 11) %></li>
<% } %>
<% } %>
/ul>
// 转化为html后
<ul>
<li>0</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
</ul>


下面来考虑下,其实形成上述的
html
的话,利用
js
拼接字符串就可以做到,那么对于模板来说,是不是可以将模板内的
js
代码转化为原生
js
代码从而来实现呢?没错,这个简单的模板引擎干的就是这个事情,就是将模板内的被
<% %>
包裹的
js
代码执行,从而将模板转为
html


但是,如何做呢?可以采用
Function(参数名, 函数主体)
,虽说这个平时不怎么使用,但是在这个情况下是最好的选择,将模板内的
js
代码解析提取后当做参数传入到
Function
中:

/**
* 将模板引擎转化为可用dom字符串
*
* @param {String} tpl
* @param {Object} data 数据对象,为键值对形式,键值为数据名
* @return {String}
*/
function tpl2dom (tpl, data) {
var nbspRE = /\s{2,}/g,
quotRE = /\"/g,
main = "";    // 函数主体
function fn (d) {
var i, keys = [], vals = [];
for (i in d) {
keys.push(i);      // 参数名
vals.push(d[i]);   // 参数对应的值
}
return (new Function(keys, main)).apply(d, vals);
}
if (!main) {
tpl = tpl.replace(/</g, '<').replace(/>/g, '>'); // 将<和>替换
var tpls = tpl.split("<%"),
len = tpls.length;
main = `var res = "${tpls[0].replace(nbspRE, '').replace(quotRE, "\'")}";\n`;
// res就是拼接的html字符串
for (var i = 1; i < len; i++) {
var p = tpls[i].split("%>");
// 发现第一个字符为`=`号时,直接进行赋值操作
main += 0x3D === p[0].charCodeAt(0) ?
`\nres += ${p[0].substr(1)}`:
`\n${p[0].replace(/\r\n/g, "").trim()}`;
main += `\nres += "${p[p.length - 1]
.replace(nbspRE, '')
.replace(quotRE, "\'")
.replace(/[\r\n]/g, '')}"`;
}
main += "\nreturn res;";
}
return data ? fn(data) : fn();
}


利用
tpl2dom
函数可以得到
html
,那下面在写一个函数来将
html
转为
dom
节点。从下面的函数可以看出的就是,这个模板引擎所解析的模板,只能有一个
dom
根节点:

var cacheDiv = document.createElement("div");

/**
* 将可用dom字符串转为dom节点
*
* @param {String} str
* @return {DOM}
*/
function str2dom (str) {
cacheDiv.innerHTML = "";
cacheDiv.innerHTML = str;
// 由此可看出,模板只能有一个根节点
return cacheDiv.childNodes[0];
}


到此为止,模板引擎就基本完成了,既然是仿照的
ejs
,那么在来完善下,比如
img_tag


// 注意,实现该函数需放到全局
function img_tag (url) {
return `<img src="${url}" />`
}


完整代码请见github,当然这个模板引擎只是一个玩物,并不能用于生产,比如,根本没有考虑到编译时模板语法不正确,渲染过程中出错时如何定位到具体位置。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  模板引擎