心血来潮,来写一个模板引擎吧!

功能就不多说了

实现思路

从最简单的开始吧

假设一个模板长这样

<h1><% var world = 'world!'; %> hello <%= world %></h1>

模板转化后应该到这样的:

1
2
3
4
5
6
// 假设输出的结果存放在变量p中
p += '<h1>';
var world = 'world!';
p += ' hello ';
p += world;
p += '</h1>';

总结一下这个过程,其实就只有三条规则:

  1. %>...<% 转换为 %>p += '...';<%, 起始和结束同此, 将内容直接作为输出
  2. <%...%> 转换为 ...;, 逻辑处理, 直接执行
  3. <%=...%> 转换为 p += (...);, 将运算结果作为输出

实现

实现就很简单了把上面的规则写成代码,

1
2
3
4
tpl.replace(/(\s)+/g, " ") // 主要是为了处理换行
.replace(/(\%\>|^)(.+?)(\<\%|$)/g, "$1p += '$2';$3")
.replace(/\<\%([^=](.(?!\<\%))+)\%\>/g, "$1;")
.replace(/\<\%\=((.(?!\<\%))+)\%\>/g, "p += ($1);")

然后包装一下

1
2
3
4
5
6
7
8
function render (tpl) {
return new Function("data", "var p = ''; with(data){" +
tpl.replace(/(\s)+/g, " ")
.replace(/(\%\>|^)(.+?)(\<\%|$)/g, "$1p += '$2';$3")
.replace(/\<\%([^=](.(?!\<\%))+)\%\>/g, "$1;")
.replace(/\<\%\=((.(?!\<\%))+)\%\>/g, "p += ($1);") +
"} return p; ");
}

完成!

测试

1
2
3
4
5
6
7
8
9
// 模板
var tpl = `<ul>
<% for (var i = 0; i < list.length; i++) { %>
<li><%= list[i] %></li>
<% } %>
</ul>`;
var renderFn = render(tpl); // 获取渲染函数
var renderResult = renderFn({list:["a", "b", "c", "d"]}); // 渲染结果
// <ul> <li>a</li> <li>b</li> <li>c</li> <li>d</li> </ul>

大功告成

当然中间还是遇到了很多问题,不过最终的结果还是非常好的,这个轮子项目已经放在github仓库