这是一个小巧且强大的js模板引擎,是我在2015年设计编写,那时候我还是个前端萌新,还记得那天夕阳下的奔跑,那是我逝去的青春........
haole ,言归正传,这个模板引擎还算不错,已经用在了不少的项目中,主要优点就是小巧快捷,
(function () {
var config = {
open: '{{',
close: '}}'
}
var regEach1 = /^\s*each\s+(?<name>.+?)(\s+as\s+(?<val>[\w|\$]+?)){0,1}(\s*,\s*(?<key>[\w|\$]+?)){0,1}(\s*,\s*(?<idx>[\w|\$]+?)){0,1}\s*$/
var regEach2 = /^\s*\/each\s*$/
var regIf1 = /^\s*if\s+(.+)\s*$/
var regIf2 = /^\s*\/if\s*$/
var regElse = /^\s*else\s*$/
var regElseIf = /^\s*else\s*if\s+(.+)\s*$/
var $sz = /^((?!if|for|else|switch|case|break|{|}|var\s+).)*$/; //不包含这些关键字
var seq = 1; //序列值,用于记录each共处理多少次
function uuid() {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 8; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4";
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
//s[8] = s[13] = s[18] = s[23] = "";
var uuid = s.join("");
return uuid;
}
/**
* 模板渲染
* @param {string} content 模板
* @param {any} data 数据
* @returns string
*/
function render(content, data) {
data = data || {};
var list = ['this.tpl = "";'];
for (var key in data){
list.push('var '+key+'=data["'+key+'"]');
}
var codeArr = transform(content); // 代码分割项数组
for (var i = 0, len = codeArr.length; i < len; i++) {
var item = codeArr[i]; // 当前分割项
if (item.type === 1) { // js逻辑
//处理 each,if,else,elseif
var match,block
if (match = regEach1.exec(item.txt)) {
console.log(match)
var items='items'+uuid()+seq++;
var name = match.groups.name
var val=match.groups.val||'val'
var key=match.groups.key||'key'
var idx=match.groups.idx||'index'
block = `var ${idx}=-1;var ${items}=${name};for(var ${key} in ${items}){ var ${val}=${items}[${key}];${idx}++;`;
list.push(block)
} else if (match = regEach2.exec(item.txt)) {
list.push('}');
} else if (match = regIf1.exec(item.txt)) {
block = 'if(' + match[1] + '){'
list.push(block)
} else if (match = regIf2.exec(item.txt)) {
list.push('}')
} else if (match = regElse.exec(item.txt)) {
list.push('}else{')
} else if (match = regElseIf.exec(item.txt)) {
block = '}else if(' + match[1] + '){'
list.push(block)
} else if ($sz.exec(item.txt)){ //匹配输出表达式
list.push('this.tpl+='+item.txt);
}else{
list.push(item.txt);
}
} else if (item.type === 2) { // js占位
var txt = 'this.tpl+=' + item.txt + ';';
list.push(txt);
} else { //文本
var txt = 'this.tpl+="' +
item.txt.replace(/"/g, '\\"') +
'";';
list.push(txt);
}
}
list.push('return this.tpl;');
//console.log(list.join('\n'))
var obj = {
fun:new Function('data', list.join('\n'))
}
return obj.fun(data);
}
/**
* 从原始模板中提取 文本/js 部分
* @param {string} content
* @returns {Array<{type:number,txt:string}>}
*/
function transform(content) {
var arr = []; //返回的数组,用于保存匹配结果
//var reg = /\{\{\s*((.|\n)*?)\s*\}\}/g; //用于匹配js代码的正则
var str = config.open+'\\s*((.|\\n)*?)\\s*'+config.close
var reg = new RegExp(str,'g')
var match; //当前匹配到的match
var nowIndex = 0; //当前匹配到的索引
while (match = reg.exec(content)) {
// 保存当前匹配项之前的普通文本/占位
appendTxt(arr, content.substring(nowIndex, match.index));
//保存当前匹配项
var item = {
type: 1, // 类型 1- js代码块 2- js表达式 null- 文本
txt: match[1] // 内容
};
var first = match[1].substr(0, 1)
if (first == ':' || first == '=') { // 如果是js占位
item.type = 2;
item.txt = item.txt.substr(1);
}
arr.push(item);
//更新当前匹配索引
nowIndex = match.index + match[0].length;
}
console.log(content)
//保存文本尾部
appendTxt(arr, content.substr(nowIndex));
return arr;
}
/**
* 普通文本添加到数组,对换行部分进行转义
*
* @param {Array<{type:number,txt:string}>} list
* @param {string} content
*/
function appendTxt(list, content) {
content = content.replace(/\r?\n/g, "\\n");
list.push({txt: content});
}
window.cctpl={
render:render,
config:config,
}
})();
编写模板
<script id="tpl1" type="text/tpl">
{{ each arr as val,key,idx }}
<li>{{val.name}}-{{key}}-{{idx}}</li>
{{ /each }}
<div>{{name}}</div>
</script>
数据合成,这里用了jquery辅助获取
$(function (){
var tmp = $('#tpl1').html().trim();
var ret = cctpl.render(tmp,{name:'天穹之光',arr:[{name:'关羽'},{name:'张飞'},{name:'赵云'}]})
console.log(ret)
})