写一个简单的模板引擎
Posted liuyongjia
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了写一个简单的模板引擎相关的知识,希望对你有一定的参考价值。
写一个简单的模板引擎
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
的做法,我们可以整理一下自己的思路:
- 通过正则把插值和实际字符串拆开
- 通过
eval
或者new Function()
实现插值的计算 - 通过 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
,所以把参数分解后传入了。
(完)。
以上是关于写一个简单的模板引擎的主要内容,如果未能解决你的问题,请参考以下文章