模板解析
解决的问题:
将data和js+html片段解析成html片段,完成赋值和js逻辑,如下所示:
输入:
var data = {
name: ‘hwm‘,
articles: [{ title: ‘the first one‘ }, { title: ‘the last one‘ }]
};
tpl = ‘<p>Hi my name is <%this.name%>.</p><p>My articles are listed as followed:</p>‘
+ ‘<ul>‘
+ ‘<%for(var i = 0; i < this.articles.length; i++) {%>‘
+ ‘<li><%this.articles[i].title%></li>‘
+ ‘<%}%>‘
+ ‘</ul>‘;
输出:
核心思想:
(1)将js片段、赋值片段、html片段解析出来,主要依赖于正则表达式
// 匹配被标识的片段
var reg = /<%(.*?(?=%>))%>/g;
// 匹配if、for等开头的逻辑代码
var logicalReg = /^(( )?(if|for|while|else|{|}|switch|case|break))(.*)?/g;
(2)解析结果均是字符串,通过new Function()的方法,可以将字符串传入函数,当做函数体然后执行
于是在字符串的逻辑就是构建一个数组,然后将html和赋值片段push到数组,将逻辑片段添加到字符串,然后通过call将data传入函数体并执行;
// 匹配被标识的片段
var reg = /<%(.*?(?=%>))%>/g;
// 匹配if、for等开头的逻辑代码
var logicalReg = /^(( )?(if|for|while|else|{|}|switch|case|break))(.*)?/g;
var match;
var result = ‘var result = [];\n‘;
var index = 0;
// 使用exec方法,得到匹配内容和匹配结果
while (match = reg.exec(tpl)) {
addCode(tpl.slice(index, match.index));
addCode(match[1], true);
index = match.index + match[0].length;
}
addCode(tpl.slice(index, tpl.length));
result += ‘return result.join("")‘;
result = result.replace(/[\t\n\r]/g, ‘‘);
return new Function(result);
function addCode(code, isJs) {
// 保证赋值代码不被引号包裹,js逻辑代码直接拼在字符串中
isJs ? result += (code.match(logicalReg) ? code + ‘\n‘ : ‘result.push(‘ + code + ‘);\n‘)
: result += ‘result.push("‘ + code.replace(/"/g, ‘\\"‘) + ‘");\n‘;
}
(3)建立缓存,当数据变化,模板没有变化时,直接从缓存中调用即可,无需重复解析;
(function () {
var cache = {};
this.tmpl = function (id, tpl, data) {
var parser = cache[id] || function () {
// 解析代码,同上
}();
cache[id] = parser;
return parser.call(data);
};
})();
备注:主要参考文章链接如下
http://ejohn.org/blog/javascript-micro-templating/
http://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line