写一个简单的模板引擎

写一个简单的模板引擎

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,所以把参数分解后传入了。

(完)。

原文地址:https://www.cnblogs.com/liuyongjia/p/10962850.html

时间: 2024-10-07 08:25:33

写一个简单的模板引擎的相关文章

自己写的简单的模板引擎

开发网站需要用到模板引擎,自己写了一个简单的.功能很简单,只做了两件事,一个是替换Model一个是替换List,支持List嵌套. html模板示例代码如下: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ut

一步一步写一个简单通用的makefile(三)

上一篇一步一步写一个简单通用的makefile(二) 里面的makefile 实现对通用的代码进行编译,这一章我将会对上一次的makefile 进行进一步的优化. 优化后的makefile: #Hellomake #Magnum, 2014-10-20 # 指令编译器和选项 CC=gcc CFLAGS=-Wall # 需要链接库的库名,比如libm.a,就是-lm,需要去掉前面的lib和后面的.a LIBS=-lm # 设置默认搜索头文件的路径,优先是这个,然后是系统路径 IncludeDir

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [plain] vie

(2)自己写一个简单的servle容器

自己写一个简单的servlet,能够跑一个简单的servlet,说明一下逻辑. 首先是写一个简单的servlet,这就关联到javax.servlet和javax.servlet.http这两个包的类,其中一个比较重要的接口就是:javax.servlet.Servlet,所有的servlet必须实现实现或者继承实现该接口的类. Servlet接口有五个方法: public void init(ServletConfig config) throws ServletException publi

分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”

这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业不是百度来的,我只是百度了一些示例代码的意思,怎么用!算了,越解释万一越黑呢!哈哈O(∩_∩)O哈哈~) ----------------------------------------------------------------分界线------------------------------

Java写一个简单学生管理系统

其实作为一名Java的程序猿,无论你是初学也好,大神也罢,学生管理系统一直都是一个非常好的例子,初学者主要是用数组.List等等来写出一个简易的学生管理系统,二.牛逼一点的大神则用数据库+swing来做一个有界面的学生管理系统.其实都并不会太难. 今天我就先写一个简单的用List来实现学生管理系统: 首先,管理系统是针对学生对象的,所以我们先把学生对象就写出来: package bean; public class Student { String name; String studentId;

DuiVision开发教程(2)-如何写一个简单的界面程序

基于DuiVision界面库开发的界面程序主要包括如下几部分内容: 1.资源定义,包括图片资源.各个窗口界面的xml定义文件 2.事件处理类代码,用于处理界面响应消息 3.其他业务逻辑代码 下面举例说明如何写一个简单的界面程序. 第一步:使用VC向导创建一个有两个tab页面的DuiVision工程 向导生成的解决方案文件如下: 默认有两个工程,分别是DuiVision库和应用程序工程.自动生成的代码目录中bin目录下的内容那个如下,bkimg目录存放窗口背景图片,skins目录存放图片资源,xm

使用Direct2D实现一个简单第一人称引擎

这篇文章是对"a first person engine in 265 lines"[1]的一个Direct2D版的移植.看到这篇文章我立刻就想到了QUAKE,当然QUAKE使用了比这个更高效的技术.但是这个技术无疑更简单,更娱乐,同时也算是一种RayCast原理的使用.于是就想到用Direct2D做一个看看. 由于对Html5不熟,同时参考了Fredrik Wallgren[2]. 同时也参考了MS提供的Direct2D事例[3]. 代码托管:https://d2dfpsengine

linux设备驱动第三篇:如何写一个简单的字符设备驱动?

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [email prot