自己动手撸个模板引擎(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,当然这个模板引擎只是一个玩物,并不能用于生产,比如,根本没有考虑到编译时模板语法不正确,渲染过程中出错时如何定位到具体位置。
相关文章推荐
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(一)
- 由浅入深:自己动手开发模板引擎——解释型模板引擎(二)
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(二)
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(三)
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(三)
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(四)
- 由浅入深:自己动手开发模板引擎——解释型模板引擎
- 自己动手开发模板引擎
- 由浅入深:自己动手开发模板引擎——什么是模板引擎
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(一)
- 由浅入深:自己动手开发模板引擎——解释型模板引擎(三)
- 由浅入深:自己动手开发模板引擎——解释型模板引擎(一)
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(四)
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(二)
- 由浅入深:自己动手开发模板引擎——置换型模板引擎1
- 由浅入深:自己动手开发模板引擎——置换型模板引擎(一)
- 自己动手写cocos2dx游戏引擎(一)——引擎入口
- php自己创建TPL模板引擎之初学习
- 模板方法和Callback回调应用实践-自己动手写JdbcTemplate
- 自己动手写正则表达式引擎