手写简易模板引擎

414 阅读2分钟

前言

大家想到的模板引擎有哪些?
template,handlebars, jade, ejs, underscore, nunjunks...
vue里面也有template的概念
ejs 通过<%%> 包起来用
vue通过{{}}来使用

我们今天也来整个玩玩~~

实现

我们先来理下思路:

  1. 模板里面的变量是如何替换的
  2. 模板里面的内容是如何传参的

我们这边以{% %}方式来做吧
我们先来写个简单的吧
template模板

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>字符串模板</title>
</head>
<body>
{%if(true){%}
  hello
{%} else {%}
  world
{% } %}
</body>
</html>

js模块实现:

const fs = require('fs');
let template = fs.readFileSync('./template-simple.html','utf8');

const wrapperContent = [
  'let str = "";\r\nstr+=`',
  '`;\r\nreturn str;\r\n'
];
function render(templateStr, obj) {
  templateStr = templateStr.replace(/\{\%(.+?)\%\}/g,function(){
    return '`\r\n'+arguments[1]+'\r\nstr+=`'
  });
  let fnContent = wrapperContent[0]+templateStr+wrapperContent[1];
  let newFn = new Function(fnContent);
  return newFn(obj);
}

const r = render(template);
console.log(r);

运行结果看一波

alt
上面的内容我们来总结下:

  1. 我们先把模板文件内容读取出来
  2. 列出固定头固定尾 我们后续会使用字符串拼接
  3. 写一个render函数,学习ejs一把 里面是具体的转换内容
  4. render函数内部主要是通过正则去替换掉{% %}模板标识符
  5. 最重要的一点是new Function函数,去创造了一个函数,返回了转换好的字符串

那么有人会说了,模板字符串肯定要传参的呀
那我们来咯~
模板页面:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>字符串模板</title>
</head>
<body>
{%if(true){%}
  hello
{%} else {%}
  world
{% } %}
<ul>
  {% arr.forEach(item => { %}
    <li>{{item}}</li>
  {%})%}
</ul>
</body>
</html>

js模块:

const fs = require('fs');
let template = fs.readFileSync('./template.html','utf8');

// with是申明了一块作用域,可以通过传参给作用域里面使用
const wrapperContent = [
  'let str = "";\r\nwith(val){\r\nstr+=`',
  '`;}\r\nreturn str;\r\n'
];
function render(templateStr, obj) {
   // 替换模板标识符 
  templateStr = template.replace(/\{\{(.+?)\}\}/g, function(){
    return '${'+ arguments[1] +'}';
  });
  // 替换掉模板中的变量
  templateStr = templateStr.replace(/\{\%(.+?)\%\}/g,function(){
    return '`\r\n'+arguments[1]+'\r\nstr+=`'
  });
  let fnContent = wrapperContent[0]+templateStr+wrapperContent[1];
  let newFn = new Function('val',fnContent);
  return newFn(obj);
}

const r = render(template, {arr: [1,2,3]});
console.log(r);

with的使用可以参考 看下效果

那么一个简单的模板引擎已经出来了
我们总结下:

  1. 我们在模板html里面采用ejs的写法来配置模板
  2. new Function 这边添加传参,添加with创造作用域,支持替换变量使用
  3. render函数将变量模块(本处使用{{}}包住)替换成模板字符串的${}方式,模板字符串会做处理