二话不说 轮子我都会造 还怕你面试问吗? 一天造一个轮子,干就完了。git
(计划赶不上变化 随时迭代 欢迎留言 随时摸鱼)github
为了实现视图与业务逻辑的分离,不管MVP、MVVM、MVC那个V都会使用模板引擎。线面咱们说说模板引擎的要求。面试
其实就是 将{{ }}中的值根据替换为表达式的结果。正则表达式
模板 | 结果 | |
---|---|---|
<b>{{ name }}</b> |
→ | <b>tom</b> |
<b>{{ name.toUpperCase() }}</b> |
→ | <b>TOM</b> |
<b>{{ '[' + name + ']' }}</b> |
→ | <b>[tom]</b> |
用测试用例表示就是这样算法
it("{{}} 表达式", () => {
const output = compile("<b>{{ name }}</b>")({ name: "tom" }); expect(output).toBe(`<b>tom</b>`); }); it("{{}} toUpperCase 表达式", () => { const output = compile("<b>{{ name.toUpperCase() }}</b>")({ name: "tom" }); expect(output).toBe(`<b>TOM</b>`); }); it("{{}} +链接", () => { const output = compile("<b>{{ '[' + name + ']' }}</b>")({ name: "tom" }); expect(output).toBe(`<b>[tom]</b>`); }); 复制代码
{%arr.forEach(item => {%}
<li>{{item}}</li> {%})%} 复制代码
生成结果设计模式
<li>aaa</li> <li>bbb</li> 复制代码
{% if(isShow) { %} <b>{{ name }}</b> {% } %}
复制代码
生成结果markdown
<b>tom</b>
复制代码
模板渲染的功能大概能够概括为两步:app
好比最简单的模板框架
<b>{{ name }}</b>
复制代码
生成后的渲染函数函数
generate(obj){
let str = ''; with(obj){ str+=`<b>${name}</b>`} return str; } 复制代码
执行generate结果
const ret = generate({name : 'tom'})
// 运行结果: <b>tom</b> 复制代码
咱们把编译过程其实就是经过正则表达式的匹配
第一步 将{{ xxx }}表达式 转化为ES6模板字符串 ${ xxx }
// 全局正则表达式替换
template = template.replace(/\{\{([^}]+)\}\}/g, function () { let key = arguments[1].trim(); return "${" + key + "}"; }); 复制代码
第二步 将{% %}表达式 转化为JS语句这样的就能够在模板中使用if、foreach了
好比if判断:
{% if(isShow) { %} <b>{{ name }}</b> {% } %} // 转化的函数 let str = ''; with(obj){ str+=`` if(isShow) { str+=`<b>${name}</b> ` } return str; 复制代码
实现代码
let head = `let str = '';\r\n with(obj){\r\n`;
head += "str+=`"; template = template.replace(/\{\%([^%]+)\%\}/g, function () { return "`\r\n" + arguments[1] + "\r\nstr+=`\r\n"; }); let tail = "`}\r\n return str;"; 复制代码
这里面须要一个咱们不太经常使用的语法 new Function()用于动态建立函数体 好比
new Function('arg', 'console.log(arg + 1);');
// 至关于建立了一个匿名函数 function (arg) { console.log(arg + 1); } 复制代码
完整的代码实现
template = template.replace(/\{\{([^}]+)\}\}/g, function () {
let key = arguments[1].trim(); return "${" + key + "}"; }); let head = `let str = '';\r\n with(obj){\r\n`; head += "str+=`"; template = template.replace(/\{\%([^%]+)\%\}/g, function () { return "`\r\n" + arguments[1] + "\r\nstr+=`\r\n"; }); let tail = "`}\r\n return str;"; console.log(`==========render=========`) console.log(head + template + tail); return new Function("obj", head + template + tail); 复制代码
OK 任务完成
本文使用 mdnice 排版