20 行代码实现模板引擎
实现功能 :
变量值的替换、if / else 及 for 循环等复杂操作。
// 最终效果
var opt = {
hero: {
name: 'Flash',
age: 22,
},
skills: [
'time travel',
'speed force',
'strike lightening'
]
}
var str =
'<p>'
+ '<% if( this.hero.name ) { %>'
+ '<b><% this.hero.name %></b>'
+ '<b><% this.hero.age %></b>'
+ '<% for ( let val of opt.skills ) { %>'
+ '<span><% val %></span>'
+ '<% } %>'
+ '<% } %>'
+ '</p>';
console.log(template(str, opt));
/*
<p><b>Flash</b><b>22</b><span>time travel</span><span>speed force</span><span>strike lightening</span></p>
*/
核心代码 :
function template(html, options) {
var rule = /<%(.+?)%>/g,
ruleExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g,
code = 'var arr = [];',
cursor = 0,
match;
var add = function(line, js) {
js
? (code += line.match(ruleExp) ? line : 'arr.push(' + line + ');')
: (code += line != '' ? 'arr.push("' + line.replace(/"/g, '\\"') + '");' : line);
return add;
}
while( match = rule.exec(html) ) {
add(html.slice(cursor, match.index))(match[1], true);
cursor = match.index + match[0].length;
}
add(html.substr(cursor, html.length - cursor));
code += 'return arr.join("");';
return new Function(code.replace(/[\r\t\n]/g, ' ')).apply(options);
}难点解析 :
首先搞清楚两段正则表达式所表示的意思。
var rule = /<%(.+?)%>/g 指的是匹配 <% %> 中的值。
var ruleExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g 则匹配 <% %> 中含有的 JS 语句。
利用 exec 方法将变量替换为对象属性值,注意此方法输出的是一个数组,形如下方代码。
[
"<%name%>",
" name ",
index: 21,
input:
"<p>Hello, my name is <%name%>. I\'m <%age%> years old.</p>"
]利用 cursor 与 index 分割模板与普通字符串,即当遇到 <% %> 时就分割,不属于模板内的直接用 push 方法将字符串传入 arr 中,此时注意转义 " 符号。属于模板中的值则进行进一步的判断,当值为语句时则不 push 到 arr 中,当值为变量时则需要将变量 push 到 arr 中。
利用 arr.join("") 方法将数组转换为字符串并用正则将字符串里面的空格全部替换掉。
最后使用 new Function(code).apply(options) 让字符串代码变得可执行,apply 的作用则是让值的引用指向正确的对象,即确保 this 指向正确。
原文来自:https://github.com/RetroAstro/cosmos-blog/blob/master/posts/6.md
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!