模板引擎初探
最简单:定义<%data%>做模板关键字,引擎一遇到关键字就进行文本替换。替换的值来自引擎的data参数
var templateEngine = function( tpl , data ) {
var re = /<%([^%<>]+)%>/g;
while (match = re.exec(tpl)) {
console.log( match );
tpl = tpl.replace( match[0] , data[match[1]] );
}
return tpl;
}
var template = '<p> i am <%name%>, i wanna buy <%goods%> , hello world!';
var data = {
name : 'isildur',
goods: 'coke'
};
document.write( templateEngine(template,data) );
绑定的数据想要多层嵌套怎么办?
最好是<%%>里的字符串能被javascript语义理解啦,data[name]这种方式遇到data[ property.name ]自然要跪的啦。要是<%this.property.name%>能解析就碉堡了
甚至,我们可以试着实现for之类的语句!比如这样的模板:
var template =
'My skills:' +
'<%for(var index in this.skills) {%>' +
'<a href=""><%this.skills[index]%></a>' +
'<%}%>';
怎么样,是不是很像php codeIgniter的语法 :)
那对应的,引擎里应该怎么执行这一模板呢?It’s like this:
var r = [];
r.push('My skills:');
for(var index in this.skills) {
r.push('<a href="">');
r.push(this.skills[index]);
r.push('</a>');
}
return r.join('');
有了输入输出,我们就可以尝试着写模板引擎了:
/*----------- Tpl Engine that can deal with syntax ------------*/
var syntaxTplEngine = function( tpl , data ) {
var re = /<%([^%>]+)%>/g,
code = 'var r=[];\n',
cursor = 0;
var addCode = function(line , js) {
js ? code += 'r.push(' + line + ');\n' :
code += 'r.push("' + line.replace(/"/g , '\\"') + '");\n';
}
while( match = re.exec(tpl) ) {
addCode( tpl.slice( cursor , match.index ) );
addCode( match[1] , true );
cursor = match.index + match[0].length;
}
addCode( tpl.substr(cursor, tpl.length - cursor) );
code += 'return r.join("");';
console.log( code );
return new Function( code.replace(/[\r\t\n]/g,'') ).apply(data);
};
var tpl1 = '<p>my name is <%this.name%> , I\'m <%this.profile.age%> years old';
console.log( syntaxTplEngine( tpl1 , {
name: 'isildur',
profile: {age:29}
}));
可以看到,我们手动维护了一个code字符串,遇到<%%>了就用javascript语句去执行,执行后加到code后面:
addCode( match[1], true );
最后执行code字符串,怎么执行字符串呢?看这里:
return new Function( code.replace(/[\r\t\n]/g,'') ).apply(data);
注意,Function([arg1[, arg2 [,…argn] ] , functionBody ),传入的是函数体哦! 再加上apply(data)重绑了this。code字符串按预期执行!
想执行模板里的for,if,while怎么搞?
上面只解决了模板数据有嵌套的情况。还没有解决<%%>里带for, if。为什么呢? 不是addCode( match[1] , true ) 会把诸如this.name执行了再放到code字符串中去吗?
呵呵,注意,之前我们是怎么放到code里去的呢?是r.push(line),只是把line作为了最后返回字符串的一部分而已!而这里我们想要的是去执行<%for(var i in this.skills)%>,而不是把”for(var i in this.skills)”加到最终的字符串中去!
回忆一下,我们想要的是这样才对:
for(var index in this.skills) {
r.push('<a href="">');
r.push(this.skills[index]);
r.push('</a>');
}
可不要把for…放到r.push中去哦!
这就简单了,判断下line里面有没有for,if关键字就好:
reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g;
var add = function(line, js) {
js? code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n' :
code += 'r.push("' + line.replace(/"/g, '\\"') + '");\n';
}
很强吧!?