坚持造轮子第一天 - 模板引擎

坚持造轮子第一天 - 模板引擎

二话不说 轮子我都会造 还怕你面试问吗? 一天造一个轮子,干就完了。git

看点

  • 针对大厂笔试、面试必考手写题目
  • TDD方式开发
  • 配合视频讲解

造轮子计划

(计划赶不上变化 随时迭代 欢迎留言 随时摸鱼)github

  • 框架基础
  • JS基础
    • Promise.all/race
    • 路由
    • new
    • call/apply/bind
    • Object.create
    • 深拷贝、浅拷贝
  • 算法、设计模式
    • 二分查找
    • 快排
    • 二分查找
    • 冒泡排序
    • 选择排序
    • 订阅发布
    • 斐波那契算法
    • 去重

模板引擎

为了实现视图与业务逻辑的分离,不管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>`);  });   复制代码

forEach遍历

{%arr.forEach(item => {%}
 <li>{{item}}</li> {%})%} 复制代码

生成结果设计模式

<li>aaa</li>
 <li>bbb</li> 复制代码

if判断

{% if(isShow) { %} <b>{{ name }}</b> {% } %}
复制代码

生成结果markdown

<b>tom</b> 
复制代码

功能实现

模板渲染的功能大概能够概括为两步:app

  1. 编译模板为Generate函数
  2. 执行渲染函数

好比最简单的模板框架

<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;";  复制代码

构造Generate函数

这里面须要一个咱们不太经常使用的语法 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 任务完成

关注全栈然叔 带你坚持每天造轮子 (周末休息 拒绝996)

  • 源码地址 https://github.com/su37josephxia/wheel-awesome

本文使用 mdnice 排版

相关文章
相关标签/搜索