zoukankan      html  css  js  c++  java
  • 写一个简单的模板引擎

    写一个简单的模板引擎

    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
    ${2 + 3}!dk${a}`);
    

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

    "use strict";
    
    var a = 1;
    console.log("Hi
    ".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(/[
    	]/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,所以把参数分解后传入了。

    (完)。

  • 相关阅读:
    NYOJ--703
    CDOJ--1369
    NYOJ--205
    NYOJ--520
    NYOJ--69
    CDOJ--1237
    [gist]Android SHA-1
    ConnectionAbortedError: [WinError 10053] 你的主机中的软件中止了一个已建立的连接
    SyntaxError:unexpected EOF while parsing(<string,line 0>)
    Django之get请求url的参数
  • 原文地址:https://www.cnblogs.com/liuyongjia/p/10962850.html
Copyright © 2011-2022 走看看