当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 写一个简单的模板引擎

写一个简单的模板引擎

2019年06月03日  | 移动技术网IT编程  | 我要评论
写一个简单的模板引擎 ES6 开始支持模板字符串(Template literals),支持如下的写法: javascript ; html {{=1+2}} // 或者支持循环或者判断 {{for(var i in it){}}} {{=i}} {{}}} javascript const a = ...

写一个简单的模板引擎

es6 开始支持模板字符串(template literals),支持如下的写法:

`string text ${expression} string text`;

其实在很多模板引擎中,常常会有这样需求,比如常用的 dot,使用类似的语法

<div>{{=1+2}}</div>
// 或者支持循环或者判断 {{for(var i in it){}}}
<span>{{=i}}</span>
{{}}}

简单插值的实现

我们先来看看一个模板引擎基本的实现需要什么,先不考虑循环和判断,只支持变量运算。
打开babel,输入

const a = 1;
console.log(`hi\n${2 + 3}!dk${a}`);

经过babel转义以后,可以看到

"use strict";

var a = 1;
console.log("hi\n".concat(2 + 3, "!dk").concat(a));

可以看到,babel把插值提取到 concat 入参,通过函数入参的自计算实现了 template literals。在我们的使用中,其实没法直接做到这样的效果。
但是仿造babel的做法,我们可以整理一下自己的思路:

  1. 通过正则把插值和实际字符串拆开
  2. 通过eval或者new function()实现插值的计算
  3. 通过 concat 拼接,也可以使用 string.raw

代码实现如下:

var str = "string text ${1 + 2} string text ${2 + 3} test";
function template(str) {
  var pattern = /\$\{.*?\}/g;
  var patterncapture = /\$\{(.*?)\}/g;

  // 将非插值字符串分割出来
  var strarr = str.split(pattern);

  // 将插值字符串分割出来
  var rawarr = str
    .match(patterncapture)
    .map(item => item.replace(patterncapture, "$1"));

  // eval转换
  var valuearr = rawarr.map(r => eval(r));

  // 使用reduce和concat拼接,
  return strarr.reduce(
    (acc, curr, index) => acc.concat(curr, valuearr[index] || ""),
    ""
  );
  // 或者使用string.raw
  // return string.raw({ raw: strarr }, ...valuearr);
}

console.log(template(str));

new function

上面使用eval对插值进行了求值,实际上在平时使用中,eval是不推荐的。而且用eval去解析一些循环判断和条件判断也不是很方便。
所以接下来使用new function()去构建一个模板函数。在这之前,需要说明一下new function的用法。

new function ([arg1[, arg2[, ...argn]],] functionbody)

前面传入的是函数所需要的参数,最后是函数体,函数体是一个包括函数定义的 javascript 语句字符串。
其次,根据上面的插值实现,我们可以使用字符串拼接把插值计算之后和正常的字符串拼接起来。
对于简单插值,使用{{}}包裹,而语句使用{{~}}区别。下面是一个简单实现

function render(tem, data) {
  let template = tem;
  template = template
    .replace(/[\r\n\t]/g, "")
    .replace(/\{\{~(.+?)\}\}/g, (_, p1) => {
      return '";' + p1 + ' out+="';
    })
    .replace(/\{\{(.+?)\}\}/g, (_, p1) => {
      return '"; out+=""+' + p1 + '+""; out+="';
    });
  template = 'var out=""; out += "' + template + '";return out;';
  var _render = new function(...object.keys(data), template);

  return _render(...object.keys(data).map(k => data[k]));
}

var template =
  "test array{{~for (var i in group.jobs) {}}{{group.jobs[i]}}  {{~}}} test obj {{group.jobs[1]}} {{group.name}} leader是{{leader}}";

var data = {
  group: {
    name: "group1",
    jobs: ["job1", "job2"]
  },
  leader: "张三"
};

console.log(render(template, data));

在给模板注入数据时,可以使用with(data){}的方式,我不喜欢使用with,所以把参数分解后传入了。

(完)。

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网