模板解析
解决的问题:
将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>\';
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;
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\';
}
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 () {
var cache = {};
this.tmpl = function (id, tpl, data) {
var parser = cache[id] || function () {
// 解析代码,同上
}();
cache[id] = parser;
return parser.call(data);
};
})();
cache[id] = parser;
return parser.call(data);
};
})();
备注:主要参考文章链接如下